#include "global.h"
#include "malloc.h"
#include "apprentice.h"
#include "battle.h"
#include "battle_anim.h"
#include "battle_controllers.h"
#include "battle_message.h"
#include "battle_pike.h"
#include "battle_pyramid.h"
#include "battle_setup.h"
#include "battle_tower.h"
#include "data.h"
#include "event_data.h"
#include "evolution_scene.h"
#include "field_specials.h"
#include "field_weather.h"
#include "item.h"
#include "link.h"
#include "main.h"
#include "overworld.h"
#include "m4a.h"
#include "party_menu.h"
#include "pokedex.h"
#include "pokeblock.h"
#include "pokemon.h"
#include "pokemon_animation.h"
#include "pokemon_icon.h"
#include "pokemon_summary_screen.h"
#include "pokemon_storage_system.h"
#include "random.h"
#include "recorded_battle.h"
#include "rtc.h"
#include "sound.h"
#include "string_util.h"
#include "strings.h"
#include "task.h"
#include "text.h"
#include "trainer_hill.h"
#include "util.h"
#include "constants/abilities.h"
#include "constants/battle_frontier.h"
#include "constants/battle_move_effects.h"
#include "constants/hold_effects.h"
#include "constants/item_effects.h"
#include "constants/items.h"
#include "constants/layouts.h"
#include "constants/moves.h"
#include "constants/songs.h"
#include "constants/species.h"
#include "constants/trainers.h"
#include "constants/weather.h"
#include "librfu.h"
#include "dexnav.h"
#include "day_night.h"
#include "script_pokemon_util.h"
#include "battle_evolution.h"

struct SpeciesItem
{
    u16 species;
    u16 item;
};

// this file's functions
extern u32 GetPlayerIDAsU32();
u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves);
//static u16 CalculateBoxMonChecksum(struct BoxPokemon *boxMon);
//static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType);
//static void EncryptBoxMon(struct BoxPokemon *boxMon);
//static void DecryptBoxMon(struct BoxPokemon *boxMon);
static void sub_806E6CC(u8 taskId);
static bool8 ShouldGetStatBadgeBoost(u16 flagId, u8 battlerId);
static u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move);
static bool8 ShouldSkipFriendshipChange(void);

// EWRAM vars
EWRAM_DATA static u8 sLearningMoveTableID = 0;
EWRAM_DATA u8 gPlayerPartyCount = 0;
EWRAM_DATA u8 gEnemyPartyCount = 0;
EWRAM_DATA struct Pokemon gPlayerParty[PARTY_SIZE] = {0};
EWRAM_DATA struct Pokemon gEnemyParty[PARTY_SIZE] = {0};
EWRAM_DATA struct SpriteTemplate gMultiuseSpriteTemplate = {0};
EWRAM_DATA struct Unknown_806F160_Struct *gUnknown_020249B4[2] = {NULL};

// const rom data
#include "data/battle_moves.h"

// Used in an unreferenced function in RS.
// Unreferenced here and in FRLG.
struct CombinedMove
{
    u16 move1;
    u16 move2;
    u16 newMove;
};

static const struct CombinedMove sCombinedMoves[2] =
        {
                {MOVE_EMBER, MOVE_GUST, MOVE_HEAT_WAVE},
                {0xFFFF, 0xFFFF, 0xFFFF}
        };

#include "data/pokemon/pokemon_dex.h"

const struct SpindaSpot gSpindaSpotGraphics[] =
{
    {16, 7, INCBIN_U16("graphics/spinda_spots/spot_0.bin")},
    {40, 8, INCBIN_U16("graphics/spinda_spots/spot_1.bin")},
    {22, 25, INCBIN_U16("graphics/spinda_spots/spot_2.bin")},
    {34, 26, INCBIN_U16("graphics/spinda_spots/spot_3.bin")}
};

#include "data/pokemon/item_effects.h"

const s8 gNatureStatTable[NUM_NATURES][NUM_NATURE_STATS] =
{
    // Atk Def Spd Sp.Atk Sp.Def
    {    0,  0,  0,     0,     0}, // Hardy
    {   +1, -1,  0,     0,     0}, // Lonely
    {   +1,  0, -1,     0,     0}, // Brave
    {   +1,  0,  0,    -1,     0}, // Adamant
    {   +1,  0,  0,     0,    -1}, // Naughty
    {   -1, +1,  0,     0,     0}, // Bold
    {    0,  0,  0,     0,     0}, // Docile
    {    0, +1, -1,     0,     0}, // Relaxed
    {    0, +1,  0,    -1,     0}, // Impish
    {    0, +1,  0,     0,    -1}, // Lax
    {   -1,  0, +1,     0,     0}, // Timid
    {    0, -1, +1,     0,     0}, // Hasty
    {    0,  0,  0,     0,     0}, // Serious
    {    0,  0, +1,    -1,     0}, // Jolly
    {    0,  0, +1,     0,    -1}, // Naive
    {   -1,  0,  0,    +1,     0}, // Modest
    {    0, -1,  0,    +1,     0}, // Mild
    {    0,  0, -1,    +1,     0}, // Quiet
    {    0,  0,  0,     0,     0}, // Bashful
    {    0,  0,  0,    +1,    -1}, // Rash
    {   -1,  0,  0,     0,    +1}, // Calm
    {    0, -1,  0,     0,    +1}, // Gentle
    {    0,  0, -1,     0,    +1}, // Sassy
    {    0,  0,  0,    -1,    +1}, // Careful
    {    0,  0,  0,     0,     0}, // Quirky
};

#include "data/pokemon/new_tmhm.h"
#include "data/pokemon/trainer_class_lookups.h"
#include "data/pokemon/experience_tables.h"
#include "data/pokemon/base_stats.h"
#include "data/pokemon/level_up_learnsets.h"
#include "data/pokemon/evolution.h"
#include "data/pokemon/level_up_learnset_pointers.h"

// SPECIES_NONE are ignored in the following two tables, so decrement before accessing these arrays to get the right result


const u8 gPPUpGetMask[] = {0x03, 0x0c, 0x30, 0xc0}; // Masks for getting PP Up count, also PP Max values
const u8 gPPUpSetMask[] = {0xfc, 0xf3, 0xcf, 0x3f}; // Masks for setting PP Up count
const u8 gPPUpAddMask[] = {0x01, 0x04, 0x10, 0x40}; // Values added to PP Up count

const u8 gStatStageRatios[MAX_STAT_STAGE + 1][2] =
        {
                {10, 40}, // -6, MIN_STAT_STAGE
                {10, 35}, // -5
                {10, 30}, // -4
                {10, 25}, // -3
                {10, 20}, // -2
                {10, 15}, // -1
                {10, 10}, //  0, DEFAULT_STAT_STAGE
                {15, 10}, // +1
                {20, 10}, // +2
                {25, 10}, // +3
                {30, 10}, // +4
                {35, 10}, // +5
                {40, 10}, // +6, MAX_STAT_STAGE
        };

static const u8 sDeoxysBaseStats[] =
        {
                [STAT_HP]    = 50,
                [STAT_ATK]   = 95,
                [STAT_DEF]   = 90,
                [STAT_SPEED] = 180,
                [STAT_SPATK] = 95,
                [STAT_SPDEF] = 90,
        };

const u16 gLinkPlayerFacilityClasses[NUM_MALE_LINK_FACILITY_CLASSES + NUM_FEMALE_LINK_FACILITY_CLASSES] =
        {
                // Male classes
                FACILITY_CLASS_COOLTRAINER_M,
                FACILITY_CLASS_BLACK_BELT,
                FACILITY_CLASS_CAMPER,
                FACILITY_CLASS_YOUNGSTER,
                FACILITY_CLASS_PSYCHIC_M,
                FACILITY_CLASS_BUG_CATCHER,
                FACILITY_CLASS_PKMN_BREEDER_M,
                FACILITY_CLASS_GUITARIST,
                // Female Classes
                FACILITY_CLASS_COOLTRAINER_F,
                FACILITY_CLASS_HEX_MANIAC,
                FACILITY_CLASS_PICNICKER,
                FACILITY_CLASS_LASS,
                FACILITY_CLASS_PSYCHIC_F,
                FACILITY_CLASS_BATTLE_GIRL,
                FACILITY_CLASS_PKMN_BREEDER_F,
                FACILITY_CLASS_BEAUTY
        };

const struct SpriteTemplate gUnknown_08329D98[MAX_BATTLERS_COUNT] =
        {
                {   // B_POSITION_PLAYER_LEFT
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gUnknown_082FF3A8,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {   // B_POSITION_OPPONENT_LEFT
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACA8,
                        .anims = NULL,
                        .images = gUnknown_082FF3C8,
                        .affineAnims = gUnknown_082FF694,
                        .callback = SpriteCb_WildMon,
                },
                {   // B_POSITION_PLAYER_RIGHT
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gUnknown_082FF3E8,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {   // B_POSITION_OPPONENT_RIGHT
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACA8,
                        .anims = NULL,
                        .images = gUnknown_082FF408,
                        .affineAnims = gUnknown_082FF694,
                        .callback = SpriteCb_WildMon
                },
        };

static const struct SpriteTemplate gSpriteTemplateTable_TrainerBackSprites[] =
        {
                {
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gTrainerBackPicTable_Brendan,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gTrainerBackPicTable_May,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gTrainerBackPicTable_Red,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gTrainerBackPicTable_Leaf,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gTrainerBackPicTable_RubySapphireBrendan,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gTrainerBackPicTable_RubySapphireMay,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gTrainerBackPicTable_Wally,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gTrainerBackPicTable_Steven,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gTrainerBackPicTable_Ash,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
                {
                        .tileTag = 0xFFFF,
                        .paletteTag = 0,
                        .oam = &gOamData_831ACB0,
                        .anims = NULL,
                        .images = gTrainerBackPicTable_Serena,
                        .affineAnims = gUnknown_082FF618,
                        .callback = sub_8039BB4,
                },
        };

static const u8 sSecretBaseFacilityClasses[2][5] =
        {
                {FACILITY_CLASS_YOUNGSTER, FACILITY_CLASS_BUG_CATCHER, FACILITY_CLASS_RICH_BOY, FACILITY_CLASS_CAMPER, FACILITY_CLASS_COOLTRAINER_M},
                {FACILITY_CLASS_LASS, FACILITY_CLASS_SCHOOL_KID_F, FACILITY_CLASS_LADY, FACILITY_CLASS_PICNICKER, FACILITY_CLASS_COOLTRAINER_F}
        };

static const u8 sGetMonDataEVConstants[] =
        {
                MON_DATA_HP_EV,
                MON_DATA_ATK_EV,
                MON_DATA_DEF_EV,
                MON_DATA_SPEED_EV,
                MON_DATA_SPDEF_EV,
                MON_DATA_SPATK_EV
        };

// For stat-raising items
static const u8 sStatsToRaise[] =
        {
                STAT_ATK, STAT_ATK, STAT_SPEED, STAT_DEF, STAT_SPATK, STAT_ACC
        };

// 3 modifiers each for how much to change friendship for different ranges
// 0-99, 100-199, 200+
static const s8 sFriendshipEventModifiers[][3] =
        {
                [FRIENDSHIP_EVENT_GROW_LEVEL]      = { 5,  3,  2},
                [FRIENDSHIP_EVENT_VITAMIN]         = { 5,  3,  2},
                [FRIENDSHIP_EVENT_BATTLE_ITEM]     = { 1,  1,  0},
                [FRIENDSHIP_EVENT_LEAGUE_BATTLE]   = { 3,  2,  1},
                [FRIENDSHIP_EVENT_LEARN_TMHM]      = { 1,  1,  0},
                [FRIENDSHIP_EVENT_WALKING]         = { 1,  1,  1},
                [FRIENDSHIP_EVENT_FAINT_SMALL]     = {-1, -1, -1},
                [FRIENDSHIP_EVENT_FAINT_FIELD_PSN] = {-5, -5, -10},
                [FRIENDSHIP_EVENT_FAINT_LARGE]     = {-5, -5, -10},
        };

static const u16 sHMMoves[] =
        {
                MOVE_CUT, MOVE_FLY, MOVE_SURF, MOVE_STRENGTH, MOVE_FLASH,
                MOVE_ROCK_SMASH, MOVE_WATERFALL, MOVE_DIVE, 0xFFFF
        };

static const struct SpeciesItem sAlteringCaveWildMonHeldItems[] =
        {
                {SPECIES_NONE,      ITEM_NONE},
                {SPECIES_MAREEP,    ITEM_GANLON_BERRY},
                {SPECIES_PINECO,    ITEM_APICOT_BERRY},
                {SPECIES_HOUNDOUR,  ITEM_BIG_MUSHROOM},
                {SPECIES_TEDDIURSA, ITEM_PETAYA_BERRY},
                {SPECIES_AIPOM,     ITEM_BERRY_JUICE},
                {SPECIES_SHUCKLE,   ITEM_BERRY_JUICE},
                {SPECIES_STANTLER,  ITEM_PETAYA_BERRY},
                {SPECIES_SMEARGLE,  ITEM_SALAC_BERRY},
        };

static const struct OamData sOamData_8329F20 =
        {
                .y = 0,
                .affineMode = ST_OAM_AFFINE_OFF,
                .objMode = ST_OAM_OBJ_NORMAL,
                .mosaic = 0,
                .bpp = ST_OAM_4BPP,
                .shape = SPRITE_SHAPE(64x64),
                .x = 0,
                .matrixNum = 0,
                .size = SPRITE_SIZE(64x64),
                .tileNum = 0,
                .priority = 0,
                .paletteNum = 0,
                .affineParam = 0
        };

static const struct SpriteTemplate gUnknown_08329F28 =
        {
                .tileTag = 0xFFFF,
                .paletteTag = 0xFFFF,
                .oam = &sOamData_8329F20,
                .anims = gDummySpriteAnimTable,
                .images = NULL,
                .affineAnims = gDummySpriteAffineAnimTable,
                .callback = SpriteCallbackDummy,
        };

// code
void ZeroBoxMonData(struct BoxPokemon *boxMon)
{
    u8 *raw = (u8 *)boxMon;
    u32 i;
    for (i = 0; i < sizeof(struct BoxPokemon); i++)
        raw[i] = 0;
}

void ZeroMonData(struct Pokemon *mon)
{
    u32 arg;
    ZeroBoxMonData(&mon->box);
    arg = 0;
    SetMonData(mon, MON_DATA_STATUS, &arg);
    SetMonData(mon, MON_DATA_LEVEL, &arg);
    SetMonData(mon, MON_DATA_HP, &arg);
    SetMonData(mon, MON_DATA_MAX_HP, &arg);
    SetMonData(mon, MON_DATA_ATK, &arg);
    SetMonData(mon, MON_DATA_DEF, &arg);
    SetMonData(mon, MON_DATA_SPEED, &arg);
    SetMonData(mon, MON_DATA_SPATK, &arg);
    SetMonData(mon, MON_DATA_SPDEF, &arg);
    arg = 255;
    SetMonData(mon, MON_DATA_MAIL, &arg);
}

void ZeroPlayerPartyMons(void)
{
    s32 i;
    for (i = 0; i < PARTY_SIZE; i++)
        ZeroMonData(&gPlayerParty[i]);
}

void ZeroEnemyPartyMons(void)
{
    s32 i;
    for (i = 0; i < PARTY_SIZE; i++)
        ZeroMonData(&gEnemyParty[i]);
}

static u32 MonIv3()
{
    u8 iv[NUM_STATS] = {0};
    u32 i, j;
    u32 ret = 0;
    for (i = 0; i < 3;)
    {
        j = Random() % NUM_STATS;
        if (!iv[j])
        {
            iv[j] = 1;
            i++;
        }
    }
    j = Random32();
    for (i = 0; i < NUM_STATS; i++)
    {

        if (iv[i])
        {
            ret = ret << 5 | j;
        } else
        {
            ret = ret << 5 | (j & 0x3f);
            j >>= 5;
        }

    }
    return ret;
}

void CreateMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality,
               u8 otIdType, u32 fixedOtId, bool8 ignoreRandom)
{
    u32 arg;
    u8 name[POKEMON_NAME_LENGTH + 1];
    ZeroMonData(mon);
    CreateBoxMon(&mon->box, species, level, fixedIV, hasFixedPersonality, fixedPersonality, otIdType, fixedOtId, ignoreRandom);
    SetMonData(mon, MON_DATA_LEVEL, &level);
    arg = 255;
    SetMonData(mon, MON_DATA_MAIL, &arg);
    if (fixedIV >= 32 && gBaseStats[species].eggGroup1 == EGG_GROUP_UNDISCOVERED)
    {
        SetMonData2(mon, MON_DATA_IVS, MonIv3());
    }
    for (arg = 0; arg < MAX_MON_MOVES; arg++)
    {
        SetMonData(mon, MON_DATA_PP1 + arg,
                   &gBattleMoves[GetMonData(mon, MON_DATA_MOVE1 + arg, NULL)].pp);
    }
    GetSpeciesName(name, GetBoxMonData(&mon->box, MON_DATA_SPECIES));
    SetMonData(mon, MON_DATA_NICKNAME, name);
    CalculateMonStats(mon);
}
struct GenderSpecies {
    u16 species;
    u8 gender;
};

u32 CreatePersonality(u32 otId, u8 nature, const struct GenderSpecies* gender)
{
    u32 rolls = 0;
    u32 checkRolls = 0;
    u32 personality;
    if (!(CheckBagHasItem(ITEM_SHINY_CHARM, 1) || VarGet(VAR_LURE_STEP_COUNT) > 0))
        rolls = SHINY_CHARM_REROLLS;
    if (gender != NULL && nature < NUM_NATURES)
    {
        //上一次roll的不作数
        do
        {
            
            do
            {
                personality = Random32();
            } while (nature != GetNatureFromPersonality(personality)
                     || gender->gender != GetGenderFromSpeciesAndPersonality(gender->species, personality));
            rolls++;
        } while (!IsShinyOtIdPersonality(otId, personality) && rolls < SHINY_CHARM_REROLLS);
    }
    else if (nature < NUM_NATURES)
    {
        do
        {
            do
            {
                personality = Random32();
            } while (personality % 25 != nature);
            rolls++;
        } while (!IsShinyOtIdPersonality(otId, personality) && rolls < SHINY_CHARM_REROLLS);
    }
    else
    {
        do
        {
            personality = Random32();
            rolls++;
        } while (!IsShinyOtIdPersonality(otId, personality) && rolls <= SHINY_CHARM_REROLLS);
    }
    return personality;
}

void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality,
             u8 otIdType, u32 fixedOtId, bool8 ignoreRandom)
{
//    u8 speciesName[POKEMON_NAME_LENGTH + 1];
    u32 personality;
    u32 value;
    u16 checksum;

    if (FlagGet(FLAG_ABS_RANDOM_MODE) && !ignoreRandom) //真全随机且允许随机
        species = Random() % SPECIES_CALYREX_SHADOW_RIDER + 1;

    ZeroBoxMonData(boxMon);
    
    if (otIdType == OT_ID_PRESET) //Pokemon has a preset OT ID
    {
        value = fixedOtId;
    }
    else if (otIdType == OT_ID_RANDOM_NO_SHINY)
    {
        value = Random32();
    }
    else
    {
        value = GetPlayerIDAsU32();
    }
    if (hasFixedPersonality)
        personality = fixedPersonality;
    else
        personality = CreatePersonality(value, NUM_NATURES, 0);

    SetBoxMonData(boxMon, MON_DATA_PERSONALITY, &personality);
    SetBoxMonData(boxMon, MON_DATA_OT_ID, &value);

    //checksum = CalculateBoxMonChecksum(boxMon);
    SetBoxMonData(boxMon, MON_DATA_CHECKSUM, &checksum);
//    EncryptBoxMon(boxMon);
//    GetSpeciesName(speciesName, species);
//    SetBoxMonData(boxMon, MON_DATA_NICKNAME, speciesName);
    SetBoxMonData(boxMon, MON_DATA_LANGUAGE, &gGameLanguage);
    SetBoxMonData(boxMon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName);
    SetBoxMonData(boxMon, MON_DATA_SPECIES, &species);
    SetBoxMonData(boxMon, MON_DATA_EXP, &gExperienceTables[gBaseStats[species].growthRate][level]);
    SetBoxMonData(boxMon, MON_DATA_FRIENDSHIP, &gBaseStats[species].friendship);
    value = GetCurrentRegionMapSectionId();
    SetBoxMonData(boxMon, MON_DATA_MET_LOCATION, &value);
    SetBoxMonData(boxMon, MON_DATA_MET_LEVEL, &level);
    SetBoxMonData(boxMon, MON_DATA_MET_GAME, &gGameVersion);
    value = ITEM_POKE_BALL;
    SetBoxMonData(boxMon, MON_DATA_POKEBALL, &value);
    SetBoxMonData(boxMon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);

    if (fixedIV < 32)
    {
        SetBoxMonData(boxMon, MON_DATA_HP_IV, &fixedIV);
        SetBoxMonData(boxMon, MON_DATA_ATK_IV, &fixedIV);
        SetBoxMonData(boxMon, MON_DATA_DEF_IV, &fixedIV);
        SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &fixedIV);
        SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &fixedIV);
        SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &fixedIV);
    }
    else
    {
        u32 iv;
        value = Random();

        iv = value & 0x1F;
        SetBoxMonData(boxMon, MON_DATA_HP_IV, &iv);
        iv = (value & 0x3E0) >> 5;
        SetBoxMonData(boxMon, MON_DATA_ATK_IV, &iv);
        iv = (value & 0x7C00) >> 10;
        SetBoxMonData(boxMon, MON_DATA_DEF_IV, &iv);

        value = Random();

        iv = value & 0x1F;
        SetBoxMonData(boxMon, MON_DATA_SPEED_IV, &iv);
        iv = (value & 0x3E0) >> 5;
        SetBoxMonData(boxMon, MON_DATA_SPATK_IV, &iv);
        iv = (value & 0x7C00) >> 10;
        SetBoxMonData(boxMon, MON_DATA_SPDEF_IV, &iv);
    }
	value = Random();
    
    if (gBaseStats[species].abilityHidden && (value & 3) == 2)
    {
        value = 2;
        SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value);
    } else if (gBaseStats[species].abilities[1] && (value & 1) == 1)
    {
        value = 1;
        SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value);
    }

    GiveBoxMonInitialMoveset(boxMon);
}

void CreateMonWithNature(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 nature, bool8 ignoreRandom)
{
    CreateMon(mon, species, level, fixedIV, 1, CreatePersonality(GetPlayerIDAsU32(), nature, 0), OT_ID_PLAYER_ID, 0, ignoreRandom);
}

void CreateMonWithGenderNatureLetter(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 gender, u8 nature,
                                     u8 unownLetter, bool8 ignoreRandom)
{
    u32 personality;

    if ((u8)(unownLetter - 1) < 28)
    {
        u16 actualLetter;

        do
        {
            personality = Random32();
            actualLetter = ((((personality & 0x3000000) >> 18) | ((personality & 0x30000) >> 12) | ((personality & 0x300) >> 6) | (personality & 0x3)) % 28);
        }
        while (nature != GetNatureFromPersonality(personality)
               || gender != GetGenderFromSpeciesAndPersonality(species, personality)
               || actualLetter != unownLetter - 1);
    }
    else
    {
        struct GenderSpecies s = { species, gender };
        personality = CreatePersonality(GetPlayerIDAsU32(), nature, &s);
    }

    CreateMon(mon, species, level, fixedIV, 1, personality, OT_ID_PLAYER_ID, 0, ignoreRandom);
}

// This is only used to create Wally's Ralts. and 金毛虫
void CreateMaleMon(struct Pokemon *mon, u16 species, u8 level, bool8 ignoreRandom)
{
    u32 personality;
    u32 otId;

    do
    {
        otId = Random32();
        personality = Random32();
    }
    while (GetGenderFromSpeciesAndPersonality(species, personality) != MON_MALE);
    CreateMon(mon, species, level, 32, 1, personality, OT_ID_PRESET, otId, ignoreRandom);
}

void
CreateMonWithIVsPersonality(struct Pokemon *mon, u16 species, u8 level, u32 ivs, u32 personality, bool8 ignoreRandom)
{
    CreateMon(mon, species, level, 0, 1, personality, OT_ID_PLAYER_ID, 0, ignoreRandom);
    SetMonData(mon, MON_DATA_IVS, &ivs);
    CalculateMonStats(mon);
}

void CreateMonWithIVsOTID(struct Pokemon *mon, u16 species, u8 level, u8 *ivs, u32 otId, bool8 ignoreRandom)
{
    CreateMon(mon, species, level, 0, 0, 0, OT_ID_PRESET, otId, ignoreRandom);
    SetMonData(mon, MON_DATA_HP_IV, &ivs[0]);
    SetMonData(mon, MON_DATA_ATK_IV, &ivs[1]);
    SetMonData(mon, MON_DATA_DEF_IV, &ivs[2]);
    SetMonData(mon, MON_DATA_SPEED_IV, &ivs[3]);
    SetMonData(mon, MON_DATA_SPATK_IV, &ivs[4]);
    SetMonData(mon, MON_DATA_SPDEF_IV, &ivs[5]);
    CalculateMonStats(mon);
}

void CreateMonWithEVSpread(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 evSpread, bool8 ignoreRandom)
{
    s32 i;
    s32 statCount = 0;
    u16 evAmount;
    u8 evsBits;

    CreateMon(mon, species, level, fixedIV, 0, 0, OT_ID_PLAYER_ID, 0, ignoreRandom);

    evsBits = evSpread;

    for (i = 0; i < NUM_STATS; i++)
    {
        if (evsBits & 1)
            statCount++;
        evsBits >>= 1;
    }

    evAmount = MAX_TOTAL_EVS / statCount;

    evsBits = 1;

    for (i = 0; i < NUM_STATS; i++)
    {
        if (evSpread & evsBits)
            SetMonData(mon, MON_DATA_HP_EV + i, &evAmount);
        evsBits <<= 1;
    }

    CalculateMonStats(mon);
}

void CreateBattleTowerMon(struct Pokemon *mon, struct BattleTowerPokemon *src)
{
    s32 i;
    u8 nickname[30];
    u8 language;
    u8 value;

    CreateMon(mon, src->species, src->level, 0, 1, src->personality, OT_ID_PRESET, src->otId, 0);

    for (i = 0; i < MAX_MON_MOVES; i++)
        SetMonMoveSlot(mon, src->moves[i], i);

    SetMonData(mon, MON_DATA_PP_BONUSES, &src->ppBonuses);
    SetMonData(mon, MON_DATA_HELD_ITEM, &src->heldItem);
    SetMonData(mon, MON_DATA_FRIENDSHIP, &src->friendship);

    StringCopy(nickname, src->nickname);

    if (nickname[0] == EXT_CTRL_CODE_BEGIN && nickname[1] == EXT_CTRL_CODE_JPN)
    {
        language = LANGUAGE_JAPANESE;
        StripExtCtrlCodes(nickname);
    }
    else
    {
        language = GAME_LANGUAGE;
    }

    SetMonData(mon, MON_DATA_LANGUAGE, &language);
    SetMonData(mon, MON_DATA_NICKNAME, nickname);
    SetMonData(mon, MON_DATA_HP_EV, &src->hpEV);
    SetMonData(mon, MON_DATA_ATK_EV, &src->attackEV);
    SetMonData(mon, MON_DATA_DEF_EV, &src->defenseEV);
    SetMonData(mon, MON_DATA_SPEED_EV, &src->speedEV);
    SetMonData(mon, MON_DATA_SPATK_EV, &src->spAttackEV);
    SetMonData(mon, MON_DATA_SPDEF_EV, &src->spDefenseEV);
    value = src->abilityNum;
    SetMonData(mon, MON_DATA_ABILITY_NUM, &value);
    value = src->hpIV;
    SetMonData(mon, MON_DATA_HP_IV, &value);
    value = src->attackIV;
    SetMonData(mon, MON_DATA_ATK_IV, &value);
    value = src->defenseIV;
    SetMonData(mon, MON_DATA_DEF_IV, &value);
    value = src->speedIV;
    SetMonData(mon, MON_DATA_SPEED_IV, &value);
    value = src->spAttackIV;
    SetMonData(mon, MON_DATA_SPATK_IV, &value);
    value = src->spDefenseIV;
    SetMonData(mon, MON_DATA_SPDEF_IV, &value);
    MonRestorePP(mon);
    CalculateMonStats(mon);
}

void CreateBattleTowerMon2(struct Pokemon *mon, struct BattleTowerPokemon *src, bool8 lvl50)
{
    s32 i;
    u8 nickname[30];
    u8 level;
    u8 language;
    u8 value;

    if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_50)
        level = GetFrontierEnemyMonLevel(gSaveBlock2Ptr->frontier.lvlMode);
    else if (lvl50)
        level = 50;
    else
        level = src->level;

    CreateMon(mon, src->species, level, 0, 1, src->personality, OT_ID_PRESET, src->otId, 0);

    for (i = 0; i < MAX_MON_MOVES; i++)
        SetMonMoveSlot(mon, src->moves[i], i);

    SetMonData(mon, MON_DATA_PP_BONUSES, &src->ppBonuses);
    SetMonData(mon, MON_DATA_HELD_ITEM, &src->heldItem);
    SetMonData(mon, MON_DATA_FRIENDSHIP, &src->friendship);

    StringCopy(nickname, src->nickname);

    if (nickname[0] == EXT_CTRL_CODE_BEGIN && nickname[1] == EXT_CTRL_CODE_JPN)
    {
        language = LANGUAGE_JAPANESE;
        StripExtCtrlCodes(nickname);
    }
    else
    {
        language = GAME_LANGUAGE;
    }

    SetMonData(mon, MON_DATA_LANGUAGE, &language);
    SetMonData(mon, MON_DATA_NICKNAME, nickname);
    SetMonData(mon, MON_DATA_HP_EV, &src->hpEV);
    SetMonData(mon, MON_DATA_ATK_EV, &src->attackEV);
    SetMonData(mon, MON_DATA_DEF_EV, &src->defenseEV);
    SetMonData(mon, MON_DATA_SPEED_EV, &src->speedEV);
    SetMonData(mon, MON_DATA_SPATK_EV, &src->spAttackEV);
    SetMonData(mon, MON_DATA_SPDEF_EV, &src->spDefenseEV);
    value = src->abilityNum;
    SetMonData(mon, MON_DATA_ABILITY_NUM, &value);
    value = src->hpIV;
    SetMonData(mon, MON_DATA_HP_IV, &value);
    value = src->attackIV;
    SetMonData(mon, MON_DATA_ATK_IV, &value);
    value = src->defenseIV;
    SetMonData(mon, MON_DATA_DEF_IV, &value);
    value = src->speedIV;
    SetMonData(mon, MON_DATA_SPEED_IV, &value);
    value = src->spAttackIV;
    SetMonData(mon, MON_DATA_SPATK_IV, &value);
    value = src->spDefenseIV;
    SetMonData(mon, MON_DATA_SPDEF_IV, &value);
    MonRestorePP(mon);
    CalculateMonStats(mon);
}

void CreateApprenticeMon(struct Pokemon *mon, const struct Apprentice *src, u8 monId)
{
    s32 i;
    u16 evAmount;
    u8 language;
    u32 otId = gApprentices[src->id].otId;
    u32 personality = ((gApprentices[src->id].otId >> 8) | ((gApprentices[src->id].otId & 0xFF) << 8))
                      + src->party[monId].species + src->number;

    CreateMon(mon,
              src->party[monId].species,
              GetFrontierEnemyMonLevel(src->lvlMode - 1),
              0x1F,
              TRUE,
              personality,
              OT_ID_PRESET,
              otId, 0);

    SetMonData(mon, MON_DATA_HELD_ITEM, &src->party[monId].item);
    for (i = 0; i < MAX_MON_MOVES; i++)
        SetMonMoveSlot(mon, src->party[monId].moves[i], i);

    evAmount = MAX_TOTAL_EVS / NUM_STATS;
    for (i = 0; i < NUM_STATS; i++)
        SetMonData(mon, MON_DATA_HP_EV + i, &evAmount);

    language = src->language;
    SetMonData(mon, MON_DATA_LANGUAGE, &language);
    SetMonData(mon, MON_DATA_OT_NAME, GetApprenticeNameInLanguage(src->id, language));
    CalculateMonStats(mon);
}

void CreateMonWithEVSpreadNatureOTID(struct Pokemon *mon, u16 species, u8 level, u8 nature, u8 fixedIV, u8 evSpread,
                                     u32 otId, bool8 ignoreRandom)
{
    s32 i;
    s32 statCount = 0;
    u8 evsBits;
    u16 evAmount;

    // i is reused as personality value
    do
    {
        i = Random32();
    } while (nature != GetNatureFromPersonality(i));

    CreateMon(mon, species, level, fixedIV, TRUE, i, OT_ID_PRESET, otId, ignoreRandom);
    evsBits = evSpread;
    for (i = 0; i < NUM_STATS; i++)
    {
        if (evsBits & 1)
            statCount++;
        evsBits >>= 1;
    }

    evAmount = MAX_TOTAL_EVS / statCount;
    evsBits = 1;
    for (i = 0; i < NUM_STATS; i++)
    {
        if (evSpread & evsBits)
            SetMonData(mon, MON_DATA_HP_EV + i, &evAmount);
        evsBits <<= 1;
    }

    CalculateMonStats(mon);
}

void ConvertPokemonToBattleTowerPokemon(struct Pokemon *mon, struct BattleTowerPokemon *dest)
{
    s32 i;
    u16 heldItem;

    dest->species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);

    if (heldItem == ITEM_ENIGMA_BERRY)
        heldItem = 0;

    dest->heldItem = heldItem;

    for (i = 0; i < MAX_MON_MOVES; i++)
        dest->moves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);

    dest->level = GetMonData(mon, MON_DATA_LEVEL, NULL);
    dest->ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
    dest->otId = GetMonData(mon, MON_DATA_OT_ID, NULL);
    dest->hpEV = GetMonData(mon, MON_DATA_HP_EV, NULL);
    dest->attackEV = GetMonData(mon, MON_DATA_ATK_EV, NULL);
    dest->defenseEV = GetMonData(mon, MON_DATA_DEF_EV, NULL);
    dest->speedEV = GetMonData(mon, MON_DATA_SPEED_EV, NULL);
    dest->spAttackEV = GetMonData(mon, MON_DATA_SPATK_EV, NULL);
    dest->spDefenseEV = GetMonData(mon, MON_DATA_SPDEF_EV, NULL);
    dest->friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
    dest->hpIV = GetMonData(mon, MON_DATA_HP_IV, NULL);
    dest->attackIV = GetMonData(mon, MON_DATA_ATK_IV, NULL);
    dest->defenseIV = GetMonData(mon, MON_DATA_DEF_IV, NULL);
    dest->speedIV  = GetMonData(mon, MON_DATA_SPEED_IV, NULL);
    dest->spAttackIV  = GetMonData(mon, MON_DATA_SPATK_IV, NULL);
    dest->spDefenseIV  = GetMonData(mon, MON_DATA_SPDEF_IV, NULL);
    dest->abilityNum = GetMonData(mon, MON_DATA_ABILITY_NUM, NULL);
    dest->personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL);
    GetMonData(mon, MON_DATA_NICKNAME, dest->nickname);
}

void CreateObedientMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId)
{
    bool32 obedient = TRUE;

    CreateMon(mon, species, level, fixedIV, hasFixedPersonality, fixedPersonality, otIdType, fixedOtId, 0);
    SetMonData(mon, MON_DATA_OBEDIENCE, &obedient);
}

bool8 ShouldIgnoreDeoxysForm(u8 caseId, u8 battlerId)
{
    switch (caseId)
    {
    case 0:
    default:
        return FALSE;
    case 1:
        if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
            return FALSE;
        if (!gMain.inBattle)
            return FALSE;
        if (gLinkPlayers[GetMultiplayerId()].id == battlerId)
            return FALSE;
        break;
    case 2:
        break;
    case 3:
        if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
            return FALSE;
        if (!gMain.inBattle)
            return FALSE;
        if (battlerId == 1 || battlerId == 4 || battlerId == 5)
            return TRUE;
        return FALSE;
    case 4:
        break;
    case 5:
        if (gBattleTypeFlags & BATTLE_TYPE_LINK)
        {
            if (!gMain.inBattle)
                return FALSE;
            if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
            {
                if (gLinkPlayers[GetMultiplayerId()].id == battlerId)
                    return FALSE;
            }
            else
            {
                if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
                    return FALSE;
            }
        }
        else
        {
            if (!gMain.inBattle)
                return FALSE;
            if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
                return FALSE;
        }
        break;
    }

    return TRUE;
}


u16 GetUnionRoomTrainerPic(void)
{
    u8 linkId;
    u32 arrId;

    if (gBattleTypeFlags & BATTLE_TYPE_x2000000)
        linkId = gUnknown_0203C7B4 ^ 1;
    else
        linkId = GetMultiplayerId() ^ 1;

    arrId = gLinkPlayers[linkId].trainerId & 7;
    arrId |= gLinkPlayers[linkId].gender << 3;
    return FacilityClassToPicIndex(gLinkPlayerFacilityClasses[arrId]);
}

u16 GetUnionRoomTrainerClass(void)
{
    u8 linkId;
    u32 arrId;

    if (gBattleTypeFlags & BATTLE_TYPE_x2000000)
        linkId = gUnknown_0203C7B4 ^ 1;
    else
        linkId = GetMultiplayerId() ^ 1;

    arrId = gLinkPlayers[linkId].trainerId & 7;
    arrId |= gLinkPlayers[linkId].gender << 3;
    return gFacilityClassToTrainerClass[gLinkPlayerFacilityClasses[arrId]];
}

void CreateObedientEnemyMon(void)
{
    s32 species = gSpecialVar_0x8004;
    s32 level = gSpecialVar_0x8005;
    s32 itemId = gSpecialVar_0x8006;

    ZeroEnemyPartyMons();
    CreateObedientMon(&gEnemyParty[0], species, level, 32, 0, 0, 0, 0);
    if (itemId)
    {
        u8 heldItem[2];
        heldItem[0] = itemId;
        heldItem[1] = itemId >> 8;
        SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, heldItem);
    }
}

/*
static u16 CalculateBoxMonChecksum(struct BoxPokemon *boxMon)
{
    u16 checksum = 0;
    union PokemonSubstruct *substruct0 = GetSubstruct(boxMon, boxMon->personality, 0);
    union PokemonSubstruct *substruct1 = GetSubstruct(boxMon, boxMon->personality, 1);
    union PokemonSubstruct *substruct2 = GetSubstruct(boxMon, boxMon->personality, 2);
    union PokemonSubstruct *substruct3 = GetSubstruct(boxMon, boxMon->personality, 3);
    s32 i;

    for (i = 0; i < 6; i++)
        checksum += substruct0->raw[i];

    for (i = 0; i < 6; i++)
        checksum += substruct1->raw[i];

    for (i = 0; i < 6; i++)
        checksum += substruct2->raw[i];

    for (i = 0; i < 6; i++)
        checksum += substruct3->raw[i];

    return checksum;
}
*/

#define CALC_STAT(base, iv, ev, statIndex, field)                   \
                                                                   \
    n = (((2 * gBaseStats[species].base + (iv) + (ev) / 4) * level) / 100) + 5; \
    n = ModifyStatByNature(nature, n, statIndex);                   \
    SetMonData(mon, field, &n);                         \


void CalculateMonStats(struct Pokemon *mon)
{
    s32 oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP, NULL);
    s32 currentHP = GetMonData(mon, MON_DATA_HP, NULL);
    s32 hpIV = (mon->box.type0.ht_HP) ? 31 : GetMonData(mon, MON_DATA_HP_IV, NULL);
    s32 hpEV = GetMonData(mon, MON_DATA_HP_EV, NULL);
    s32 attackIV = (mon->box.type0.ht_ATT) ? 31 : GetMonData(mon, MON_DATA_ATK_IV, NULL);
    s32 attackEV = GetMonData(mon, MON_DATA_ATK_EV, NULL);
    s32 defenseIV = (mon->box.type0.ht_DEF) ? 31 : GetMonData(mon, MON_DATA_DEF_IV, NULL);
    s32 defenseEV = GetMonData(mon, MON_DATA_DEF_EV, NULL);
    s32 speedIV = (mon->box.type0.ht_SPE) ? 31 : GetMonData(mon, MON_DATA_SPEED_IV, NULL);
    s32 speedEV = GetMonData(mon, MON_DATA_SPEED_EV, NULL);
    s32 spAttackIV = (mon->box.type0.ht_SPA) ? 31 : GetMonData(mon, MON_DATA_SPATK_IV, NULL);
    s32 spAttackEV = GetMonData(mon, MON_DATA_SPATK_EV, NULL);
    s32 spDefenseIV = (mon->box.type0.ht_SPD) ? 31 : GetMonData(mon, MON_DATA_SPDEF_IV, NULL);
    s32 spDefenseEV = GetMonData(mon, MON_DATA_SPDEF_EV, NULL);
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    s32 level = GetLevelFromMonExp(mon);
    u8 nature = GetMonData(mon, MON_DATA_MINTNATURE, NULL);
    s32 newMaxHP;
    s32 n;
    if (!nature)
        nature = GetNature(mon);
    SetMonData(mon, MON_DATA_LEVEL, &level);

//    if (ABILITY_NONE == GetMonAbility(mon)) //如果没有特性 随机给一个
//    {
//        u8 abilityNum;
//
//        do
//            abilityNum = Random3(3);
//        while (GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE);
//        SetMonData(mon, MON_DATA_ABILITY_NUM, &abilityNum, RANDOM_KEY);
//    }

    if (species == SPECIES_SHEDINJA)
    {
        newMaxHP = 1;
    }
    else
    {
        n = 2 * gBaseStats[species].baseHP + hpIV;
        newMaxHP = (((n + hpEV / 4) * level) / 100) + level + 10;
        if (gMain.inBattle && gBattleTypeFlags & BATTLE_TYPE_LEGENDARY && (u32)mon - (u32)gEnemyParty < PARTY_SIZE * sizeof(struct Pokemon))
            newMaxHP *= 6;
        if (gMain.inBattle && CurrentBttlerEvolutionStat(gActiveBattler) == EvolutionDynamax) {
            newMaxHP *= GetDynamaxHPBoost(gActiveBattler);
        }
    }

    gBattleScripting.levelUpHP = newMaxHP - oldMaxHP;
    if (gBattleScripting.levelUpHP == 0)
        gBattleScripting.levelUpHP = 1;

    SetMonData(mon, MON_DATA_MAX_HP, &newMaxHP);

    CALC_STAT(baseAttack, attackIV, attackEV, STAT_ATK, MON_DATA_ATK)
    CALC_STAT(baseDefense, defenseIV, defenseEV, STAT_DEF, MON_DATA_DEF)
    CALC_STAT(baseSpeed, speedIV, speedEV, STAT_SPEED, MON_DATA_SPEED)
    CALC_STAT(baseSpAttack, spAttackIV, spAttackEV, STAT_SPATK, MON_DATA_SPATK)
    CALC_STAT(baseSpDefense, spDefenseIV, spDefenseEV, STAT_SPDEF, MON_DATA_SPDEF)

    if (species == SPECIES_SHEDINJA)
    {
        if (currentHP != 0 || oldMaxHP == 0)
            currentHP = 1;
        else
            return;
    }
    else
    {
        if (currentHP == 0 && oldMaxHP == 0)
            currentHP = newMaxHP;
        else if (currentHP != 0)
            currentHP += newMaxHP - oldMaxHP;
        else
            return;
    }
    if (currentHP > newMaxHP)
        currentHP = newMaxHP;
    SetMonData(mon, MON_DATA_HP, &currentHP);
}

void BoxMonToMon(const struct BoxPokemon *src, struct Pokemon *dest)
{
    u16 i;
    u32 value = 0;
    u8 ppBonuses = GetBoxMonData(src, MON_DATA_PP_BONUSES, NULL);
    dest->box = *src;
    SetMonData(dest, MON_DATA_STATUS, &value);
    SetMonData(dest, MON_DATA_HP, &value);
    SetMonData(dest, MON_DATA_MAX_HP, &value);
    value = 255;
    SetMonData(dest, MON_DATA_MAIL, &value);
    for(i = 0; i < MAX_MON_MOVES; i++)
    {
        value = CalculatePPWithBonus(GetBoxMonData(src, MON_DATA_MOVE1 + i), ppBonuses, i);
        SetMonData(dest, MON_DATA_PP1 + i, &value);
    }
    GetBoxMonData(src, MON_DATA_NICKNAME, dest->name);
    CalculateMonStats(dest);
}

u8 GetLevelFromMonExp(struct Pokemon *mon)
{
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    u32 exp = GetMonData(mon, MON_DATA_EXP, NULL);
    s32 level = 1;
    u16 max_level = VarGet(VAR_MAX_LEVEL);
    while (level <= max_level && gExperienceTables[gBaseStats[species].growthRate][level] <= exp)
        level++;

    return level - 1;
}

u8 GetLevelFromBoxMonExp(struct BoxPokemon *boxMon)
{
    u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
    u32 exp = GetBoxMonData(boxMon, MON_DATA_EXP, NULL);
    s32 level = 1;
    u16 max_level = VarGet(VAR_MAX_LEVEL);
    while (level <= max_level && gExperienceTables[gBaseStats[species].growthRate][level] <= exp)
        level++;

    return level - 1;
}

u16 GiveMoveToMon(struct Pokemon *mon, u16 move)
{
    u16 result = GiveMoveToBoxMon(&mon->box, move);
    if (result < MOVES_COUNT)
    {
        u16 i;
        for (i = 0; i < MAX_MON_MOVES; i++)
        {
            if (GetMonData(mon, MON_DATA_MOVE1 + i, NULL) == move)
            {
                SetMonData(mon, MON_DATA_PP1 + i, &gBattleMoves[move].pp);
                break;
            }
        }
    }
    return result;
}

static u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move)
{
    s32 i;
    for (i = 0; i < MAX_MON_MOVES; i++)
    {
        u16 existingMove = GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, NULL);
        if (existingMove == MOVE_NONE)
        {
            SetBoxMonData(boxMon, MON_DATA_MOVE1 + i, &move);
//            SetBoxMonData(boxMon, MON_DATA_PP1 + i, &gBattleMoves[move].pp);
            return move;
        }
        if (existingMove == move)
            return MON_ALREADY_KNOWS_MOVE;
    }
    return MON_HAS_MAX_MOVES;
}

u16 GiveMoveToBattleMon(struct BattlePokemon *mon, u16 move)
{
    s32 i;

    for (i = 0; i < MAX_MON_MOVES; i++)
    {
        if (!mon->moves[i])
        {
            mon->moves[i] = move;
            mon->pp[i] = gBattleMoves[move].pp;
            return move;
        }
    }

    return 0xFFFF;
}

void SetMonMoveSlot(struct Pokemon *mon, u16 move, u8 slot)
{
    SetMonData(mon, MON_DATA_MOVE1 + slot, &move);
    SetMonData(mon, MON_DATA_PP1 + slot, &gBattleMoves[move].pp);
}

void SetBattleMonMoveSlot(struct BattlePokemon *mon, u16 move, u8 slot)
{
    mon->moves[slot] = move;
    mon->pp[slot] = gBattleMoves[move].pp;
}

void GiveMonInitialMoveset(struct Pokemon *mon)
{
    GiveBoxMonInitialMoveset(&mon->box);
}

void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon)
{
    u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
    s32 level = GetLevelFromBoxMonExp(boxMon);
    s32 i;

    for (i = 0; gLevelUpLearnsets[species][i].move != LEVEL_UP_END; i++)
    {
        if (gLevelUpLearnsets[species][i].level > level)
            break;
        if (GiveMoveToBoxMon(boxMon, gLevelUpLearnsets[species][i].move) == MON_HAS_MAX_MOVES)
            DeleteFirstMoveAndGiveMoveToBoxMon(boxMon, gLevelUpLearnsets[species][i].move);
    }
}

u16 MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove)
{
    u32 retVal = 0;
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    u8 level = GetMonData(mon, MON_DATA_LEVEL, NULL);

    // since you can learn more than one move per level
    // the game needs to know whether you decided to
    // learn it or keep the old set to avoid asking
    // you to learn the same move over and over again
    if (firstMove)
    {
        sLearningMoveTableID = 0;

        while (gLevelUpLearnsets[species][sLearningMoveTableID].level != level)
        {
            sLearningMoveTableID++;
            if (gLevelUpLearnsets[species][sLearningMoveTableID].move == LEVEL_UP_END)
                return 0;
        }
    }

    if (gLevelUpLearnsets[species][sLearningMoveTableID].level == level)
    {
        gMoveToLearn = gLevelUpLearnsets[species][sLearningMoveTableID].move;
        sLearningMoveTableID++;
        retVal = GiveMoveToMon(mon, gMoveToLearn);
    }

    return retVal;
}

u16 MonTryLearningNewMoveFromCandy(struct Pokemon *mon, bool8 firstMove)
{
    u32 retVal = 0;
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    u8 maxlevel = GetMonData(mon, MON_DATA_LEVEL, NULL);
    u8 minlevel = maxlevel - gSpecialVar_0x8004 + 1;

    // since you can learn more than one move per level
    // the game needs to know whether you decided to
    // learn it or keep the old set to avoid asking
    // you to learn the same move over and over again
    if (firstMove)
    {
        sLearningMoveTableID = 0;

        while (gLevelUpLearnsets[species][sLearningMoveTableID].level < minlevel)
        {
            sLearningMoveTableID++;
            if (gLevelUpLearnsets[species][sLearningMoveTableID].move == LEVEL_UP_END)
                return 0;
        }
    }

    if (gLevelUpLearnsets[species][sLearningMoveTableID].level <= maxlevel)
    {
        gMoveToLearn = gLevelUpLearnsets[species][sLearningMoveTableID].move;
        if (LEVEL_UP_END == gMoveToLearn)
            return 0;
        sLearningMoveTableID++;
        retVal = GiveMoveToMon(mon, gMoveToLearn);
    }

    return retVal;
}

void DeleteFirstMoveAndGiveMoveToMon(struct Pokemon *mon, u16 move)
{
    s32 i;
    u16 moves[MAX_MON_MOVES];
    u8 pp[MAX_MON_MOVES];
    u8 ppBonuses;

    for (i = 0; i < MAX_MON_MOVES - 1; i++)
    {
        moves[i] = GetMonData(mon, MON_DATA_MOVE2 + i, NULL);
        pp[i] = GetMonData(mon, MON_DATA_PP2 + i, NULL);
    }

    ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
    ppBonuses >>= 2;
    moves[3] = move;
    pp[3] = gBattleMoves[move].pp;

    for (i = 0; i < MAX_MON_MOVES; i++)
    {
        SetMonData(mon, MON_DATA_MOVE1 + i, &moves[i]);
        SetMonData(mon, MON_DATA_PP1 + i, &pp[i]);
    }

    SetMonData(mon, MON_DATA_PP_BONUSES, &ppBonuses);
}

void DeleteFirstMoveAndGiveMoveToBoxMon(struct BoxPokemon *boxMon, u16 move)
{
    s32 i;
    u16 moves[MAX_MON_MOVES];
    u8 pp[MAX_MON_MOVES];
    u8 ppBonuses;

    for (i = 0; i < MAX_MON_MOVES - 1; i++)
    {
        moves[i] = GetBoxMonData(boxMon, MON_DATA_MOVE2 + i, NULL);
        pp[i] = GetBoxMonData(boxMon, MON_DATA_PP2 + i, NULL);
    }

    ppBonuses = GetBoxMonData(boxMon, MON_DATA_PP_BONUSES, NULL);
    ppBonuses >>= 2;
    moves[3] = move;
    pp[3] = gBattleMoves[move].pp;

    for (i = 0; i < MAX_MON_MOVES; i++)
    {
        SetBoxMonData(boxMon, MON_DATA_MOVE1 + i, &moves[i]);
//        SetBoxMonData(boxMon, MON_DATA_PP1 + i, &pp[i]);
    }

    SetBoxMonData(boxMon, MON_DATA_PP_BONUSES, &ppBonuses);
}

u8 CountAliveMonsInBattle(u8 caseId)
{
    s32 i;
    u8 retVal = 0;

    switch (caseId)
    {
        case BATTLE_ALIVE_EXCEPT_ACTIVE:
            for (i = 0; i < MAX_BATTLERS_COUNT; i++)
            {
                if (i != gActiveBattler && !(gAbsentBattlerFlags & gBitTable[i]))
                    retVal++;
            }
            break;
        case BATTLE_ALIVE_ATK_SIDE:
            for (i = 0; i < MAX_BATTLERS_COUNT; i++)
            {
                if (GetBattlerSide(i) == GetBattlerSide(gBattlerAttacker) && !(gAbsentBattlerFlags & gBitTable[i]))
                    retVal++;
            }
            break;
        case BATTLE_ALIVE_DEF_SIDE:
            for (i = 0; i < MAX_BATTLERS_COUNT; i++)
            {
                if (GetBattlerSide(i) == GetBattlerSide(gBattlerTarget) && !(gAbsentBattlerFlags & gBitTable[i]))
                    retVal++;
            }
            break;
    }

    return retVal;
}

static bool8 ShouldGetStatBadgeBoost(u16 badgeFlag, u8 battlerId)
{
    if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_x2000000 | BATTLE_TYPE_FRONTIER))
        return FALSE;
    else if (GetBattlerSide(battlerId) != B_SIDE_PLAYER)
        return FALSE;
    else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && gTrainerBattleOpponent_A == TRAINER_SECRET_BASE)
        return FALSE;
    else if (FlagGet(badgeFlag))
        return TRUE;
    else
        return FALSE;
}

u8 GetDefaultMoveTarget(u8 battlerId)
{
    u8 opposing = BATTLE_OPPOSITE(GetBattlerPosition(battlerId) & BIT_SIDE);

    if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
        return GetBattlerAtPosition(opposing);
    if (CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_ACTIVE) > 1)
    {
        u8 position;

        if ((Random() & 1) == 0)
            position = BATTLE_PARTNER(opposing);
        else
            position = opposing;

        return GetBattlerAtPosition(position);
    }
    else
    {
        if ((gAbsentBattlerFlags & gBitTable[opposing]))
            return GetBattlerAtPosition(BATTLE_PARTNER(opposing));
        else
            return GetBattlerAtPosition(opposing);
    }
}

u8 GetMonGender(struct Pokemon *mon)
{
    return GetBoxMonGender(&mon->box);
}

u8 GetBoxMonGender(struct BoxPokemon *boxMon)
{
    u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
    u32 personality = GetBoxMonData(boxMon, MON_DATA_PERSONALITY, NULL);

    switch (gBaseStats[species].genderRatio)
    {
        case MON_MALE:
        case MON_FEMALE:
        case MON_GENDERLESS:
            return gBaseStats[species].genderRatio;
    }

    if (gBaseStats[species].genderRatio > (personality & 0xFF))
        return MON_FEMALE;
    else
        return MON_MALE;
}

u8 GetGenderFromSpeciesAndPersonality(u16 species, u32 personality)
{
    switch (gBaseStats[species].genderRatio)
    {
        case MON_MALE:
        case MON_FEMALE:
        case MON_GENDERLESS:
            return gBaseStats[species].genderRatio;
    }

    if (gBaseStats[species].genderRatio > (personality & 0xFF))
        return MON_FEMALE;
    else
        return MON_MALE;
}

u32 GetUnownSpeciesId(u32 personality)
{
    return GetUnownLetterByPersonality(personality) + SPECIES_UNOWN_A;
}

void SetMultiuseSpriteTemplateToPokemon(u16 speciesTag, u8 battlerPosition)
{
    if (gMonSpritesGfxPtr != NULL)
        gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition];
    else if (gUnknown_020249B4[0])
        gMultiuseSpriteTemplate = gUnknown_020249B4[0]->templates[battlerPosition];
    else if (gUnknown_020249B4[1])
        gMultiuseSpriteTemplate = gUnknown_020249B4[1]->templates[battlerPosition];
    else
        gMultiuseSpriteTemplate = gUnknown_08329D98[battlerPosition];

    gMultiuseSpriteTemplate.paletteTag = speciesTag;
    if (battlerPosition == B_POSITION_PLAYER_LEFT || battlerPosition == B_POSITION_PLAYER_RIGHT)
        gMultiuseSpriteTemplate.anims = gUnknown_082FF70C;
    else if (speciesTag > SPECIES_SHINY_TAG)
        gMultiuseSpriteTemplate.anims = gMonFrontAnimsPtrTable[speciesTag - SPECIES_SHINY_TAG];
    else
        gMultiuseSpriteTemplate.anims = gMonFrontAnimsPtrTable[speciesTag];
}

void SetMultiuseSpriteTemplateToTrainerBack(u16 trainerSpriteId, u8 battlerPosition)
{
    gMultiuseSpriteTemplate.paletteTag = trainerSpriteId;
    if (battlerPosition == B_POSITION_PLAYER_LEFT || battlerPosition == B_POSITION_PLAYER_RIGHT)
    {
        gMultiuseSpriteTemplate = gSpriteTemplateTable_TrainerBackSprites[trainerSpriteId];
        gMultiuseSpriteTemplate.anims = gTrainerBackAnimsPtrTable[trainerSpriteId];
    }
    else
    {
        if (gMonSpritesGfxPtr != NULL)
            gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition];
        else
            gMultiuseSpriteTemplate = gUnknown_08329D98[battlerPosition];
        gMultiuseSpriteTemplate.anims = gTrainerFrontAnimsPtrTable[trainerSpriteId];
    }
}

void SetMultiuseSpriteTemplateToTrainerFront(u16 arg0, u8 battlerPosition)
{
    if (gMonSpritesGfxPtr != NULL)
        gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition];
    else
        gMultiuseSpriteTemplate = gUnknown_08329D98[battlerPosition];

    gMultiuseSpriteTemplate.paletteTag = arg0;
    gMultiuseSpriteTemplate.anims = gTrainerFrontAnimsPtrTable[arg0];
}

/*static void EncryptBoxMon(struct BoxPokemon *boxMon)
{
    u32 i;
    for (i = 0; i < 12; i++)
    {
        boxMon->secure.raw[i] ^= boxMon->personality;
        boxMon->secure.raw[i] ^= boxMon->otId;
    }
}*/

/*static void DecryptBoxMon(struct BoxPokemon *boxMon)
{
    u32 i;
    for (i = 0; i < 12; i++)
    {
        boxMon->secure.raw[i] ^= boxMon->otId;
        boxMon->secure.raw[i] ^= boxMon->personality;
    }
}*/
/*
#define SUBSTRUCT_CASE(n, v1, v2, v3, v4)                               \
case n:                                                                 \
    {                                                                   \
    union PokemonSubstruct *substructs0 = boxMon->secure.substructs;    \
    union PokemonSubstruct *substructs1 = boxMon->secure.substructs;    \
    union PokemonSubstruct *substructs2 = boxMon->secure.substructs;    \
    union PokemonSubstruct *substructs3 = boxMon->secure.substructs;    \
    union PokemonSubstruct *substructs4 = boxMon->secure.substructs;    \
    union PokemonSubstruct *substructs5 = boxMon->secure.substructs;    \
    union PokemonSubstruct *substructs6 = boxMon->secure.substructs;    \
    union PokemonSubstruct *substructs7 = boxMon->secure.substructs;    \
    union PokemonSubstruct *substructs8 = boxMon->secure.substructs;    \
    union PokemonSubstruct *substructs9 = boxMon->secure.substructs;    \
    union PokemonSubstruct *substructs10 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs11 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs12 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs13 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs14 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs15 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs16 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs17 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs18 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs19 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs20 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs21 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs22 = boxMon->secure.substructs;   \
    union PokemonSubstruct *substructs23 = boxMon->secure.substructs;   \
                                                                        \
        switch (substructType)                                          \
        {                                                               \
        case 0:                                                         \
            substruct = &substructs ## n [v1];                          \
            break;                                                      \
        case 1:                                                         \
            substruct = &substructs ## n [v2];                          \
            break;                                                      \
        case 2:                                                         \
            substruct = &substructs ## n [v3];                          \
            break;                                                      \
        case 3:                                                         \
            substruct = &substructs ## n [v4];                          \
            break;                                                      \
        }                                                               \
        break;                                                          \
    }                                                                   \
    */

/*#define SUBSTRUCT_CASE(n, c1, c2, c3, c4) {c1, c2, c3, c4},

static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 personality, u8 substructType)
{
    union PokemonSubstruct *substruct = NULL;
    static  const u8 substructIndex[][4] =
    {
        SUBSTRUCT_CASE( 0,0,1,2,3)
        
		SUBSTRUCT_CASE( 1,0,1,3,2)
        SUBSTRUCT_CASE( 2,0,2,1,3)
        SUBSTRUCT_CASE( 3,0,3,1,2)
        SUBSTRUCT_CASE( 4,0,2,3,1)
        SUBSTRUCT_CASE( 5,0,3,2,1)
        SUBSTRUCT_CASE( 6,1,0,2,3)
        SUBSTRUCT_CASE( 7,1,0,3,2)
        SUBSTRUCT_CASE( 8,2,0,1,3)
        SUBSTRUCT_CASE( 9,3,0,1,2)
        SUBSTRUCT_CASE(10,2,0,3,1)
        SUBSTRUCT_CASE(11,3,0,2,1)
        SUBSTRUCT_CASE(12,1,2,0,3)
        SUBSTRUCT_CASE(13,1,3,0,2)
        SUBSTRUCT_CASE(14,2,1,0,3)
        SUBSTRUCT_CASE(15,3,1,0,2)
        SUBSTRUCT_CASE(16,2,3,0,1)
        SUBSTRUCT_CASE(17,3,2,0,1)
        SUBSTRUCT_CASE(18,1,2,3,0)
        SUBSTRUCT_CASE(19,1,3,2,0)
        SUBSTRUCT_CASE(20,2,1,3,0)
        SUBSTRUCT_CASE(21,3,1,2,0)
        SUBSTRUCT_CASE(22,2,3,1,0)
        SUBSTRUCT_CASE(23,3,2,1,0)
		
    };
    substruct = &boxMon->secure.substructs[substructIndex[*//*personality % 24*//*0][substructType]];
    return substruct;
}*/



u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
{
    s32 i;
    u32 retVal = 0;
    struct PokemonSubstruct0 *substruct0 = &boxMon->type0;
    struct PokemonSubstruct0 *substruct1 = &boxMon->type0;
    struct PokemonSubstruct0 *substruct2 = &boxMon->type0;
    struct PokemonSubstruct0 *substruct3 = &boxMon->type0;

    // Any field greater than MON_DATA_ENCRYPT_SEPARATOR is encrypted and must be treated as such
    /*if (field > MON_DATA_ENCRYPT_SEPARATOR)
    {
        substruct0 = &(GetSubstruct(boxMon, boxMon->personality, 0)->type0);
        substruct1 = &(GetSubstruct(boxMon, boxMon->personality, 1)->type1);
        substruct2 = &(GetSubstruct(boxMon, boxMon->personality, 2)->type2);
        substruct3 = &(GetSubstruct(boxMon, boxMon->personality, 3)->type3);

        DecryptBoxMon(boxMon);

		
        if (CalculateBoxMonChecksum(boxMon) != boxMon->checksum)
        {
            boxMon->isBadEgg = 1;
            boxMon->isEgg = 1;
            substruct3->isEgg = 1;
			EncryptBoxMon(boxMon);
        }
    }*/

    switch (field)
    {
        case MON_DATA_PERSONALITY:
            retVal = boxMon->personality;
            break;
        case MON_DATA_OT_ID:
            retVal = GetPlayerIDAsU32();
            break;
        case MON_DATA_NICKNAME:
        {
            if (boxMon->isBadEgg)
            {
                for (retVal = 0;
                     retVal < POKEMON_NAME_LENGTH && gText_BadEgg[retVal] != EOS;
                     data[retVal] = gText_BadEgg[retVal], retVal++) {}

                data[retVal] = EOS;
            }
            else if (boxMon->isEgg)
            {
                StringCopy(data, gText_EggNickname);
                retVal = StringLength(data);
            }
            else if (boxMon->language == LANGUAGE_JAPANESE)
            {
                data[0] = EXT_CTRL_CODE_BEGIN;
                data[1] = EXT_CTRL_CODE_JPN;

                retVal += GetMonName(boxMon, &data[2]);

                data[retVal++] = EXT_CTRL_CODE_BEGIN;
                data[retVal++] = EXT_CTRL_CODE_ENG;
                data[retVal] = EOS;
            }
            else
            {
                retVal = GetMonName(boxMon, data);

                data[retVal] = EOS;
            }
            break;
        }
        case MON_DATA_LANGUAGE:
            retVal = boxMon->language;
            break;
        case MON_DATA_SANITY_IS_BAD_EGG:
            retVal = boxMon->isBadEgg;
            break;
        case MON_DATA_SANITY_HAS_SPECIES:
            retVal = boxMon->hasSpecies;
            break;
        case MON_DATA_SANITY_IS_EGG:
            retVal = boxMon->isEgg;
            break;
        case MON_DATA_OT_NAME:
        {
            retVal = 0;

            while (retVal < PLAYER_NAME_LENGTH)
            {
                /*data[retVal] = boxMon->otName[retVal];*/data[retVal] = gSaveBlock2Ptr->playerName[retVal];
                retVal++;
            }

            data[retVal] = EOS;
            break;
        }
        case MON_DATA_MARKINGS:
            retVal = boxMon->markings;
            break;
        /*case MON_DATA_CHECKSUM:
            retVal = boxMon->checksum;
            break;*/
        case MON_DATA_ENCRYPT_SEPARATOR:
            retVal = boxMon->unknown;
            break;
        case MON_DATA_SPECIES:
            retVal = boxMon->isBadEgg ? SPECIES_EGG : substruct0->species;
            break;
        case MON_DATA_HELD_ITEM:
            retVal = substruct0->heldItem;
            break;
        case MON_DATA_EXP:
            retVal = substruct0->experience;
            break;
        case MON_DATA_PP_BONUSES:
            retVal = substruct0->ppBonuses;
            break;
        case MON_DATA_FRIENDSHIP:
            retVal = substruct0->friendship;
            break;
        case MON_DATA_MOVE1:
            retVal = substruct1->moves0;
            break;
        case MON_DATA_MOVE2:
            retVal = substruct1->moves1;
            break;
        case MON_DATA_MOVE3:
            retVal = substruct1->moves2;
            break;
        case MON_DATA_MOVE4:
            retVal = substruct1->moves3;
            break;
//        case MON_DATA_PP1:
//        case MON_DATA_PP2:
//        case MON_DATA_PP3:
//        case MON_DATA_PP4:
//            retVal = substruct1->pp[field - MON_DATA_PP1];
//            break;
        case MON_DATA_HP_EV:
            retVal = substruct2->hpEV;
            break;
        case MON_DATA_ATK_EV:
            retVal = substruct2->attackEV;
            break;
        case MON_DATA_DEF_EV:
            retVal = substruct2->defenseEV;
            break;
        case MON_DATA_SPEED_EV:
            retVal = substruct2->speedEV;
            break;
        case MON_DATA_SPATK_EV:
            retVal = substruct2->spAttackEV;
            break;
        case MON_DATA_SPDEF_EV:
            retVal = substruct2->spDefenseEV;
            break;
        case MON_DATA_COOL:
            retVal = substruct2->cool;
            break;
        case MON_DATA_BEAUTY:
            retVal = substruct2->beauty;
            break;
        case MON_DATA_CUTE:
            retVal = substruct2->cute;
            break;
        case MON_DATA_SMART:
            retVal = substruct2->smart;
            break;
        case MON_DATA_TOUGH:
            retVal = substruct2->tough;
            break;
        case MON_DATA_SHEEN:
            retVal = substruct2->sheen;
            break;
        case MON_DATA_POKERUS:
            retVal = substruct3->pokerus;
            break;
        case MON_DATA_MET_LOCATION:
            retVal = substruct3->metLocation;
            break;
        case MON_DATA_MET_LEVEL:
            retVal = substruct3->metLevel;
            break;
        case MON_DATA_MET_GAME:
            retVal = substruct3->metGame;
            break;
        case MON_DATA_POKEBALL:
            retVal = substruct0->pokeball;
            break;
        case MON_DATA_OT_GENDER:
            retVal = substruct3->otGender;
            break;
        case MON_DATA_HP_IV:
            retVal = substruct3->hpIV;
            break;
        case MON_DATA_ATK_IV:
            retVal = substruct3->attackIV;
            break;
        case MON_DATA_DEF_IV:
            retVal = substruct3->defenseIV;
            break;
        case MON_DATA_SPEED_IV:
            retVal = substruct3->speedIV;
            break;
        case MON_DATA_SPATK_IV:
            retVal = substruct3->spAttackIV;
            break;
        case MON_DATA_SPDEF_IV:
            retVal = substruct3->spDefenseIV;
            break;
        case MON_DATA_IS_EGG:
            retVal = substruct3->isEgg;
            break;
        case MON_DATA_ABILITY_NUM:
            retVal = substruct3->abilityNum;
            break;
        case MON_DATA_COOL_RIBBON:
            retVal = substruct3->coolRibbon;
            break;
        case MON_DATA_BEAUTY_RIBBON:
            retVal = substruct3->beautyRibbon;
            break;
        case MON_DATA_CUTE_RIBBON:
            retVal = substruct3->cuteRibbon;
            break;
        case MON_DATA_SMART_RIBBON:
            retVal = substruct3->smartRibbon;
            break;
        case MON_DATA_TOUGH_RIBBON:
            retVal = substruct3->toughRibbon;
            break;
        case MON_DATA_CHAMPION_RIBBON:
            retVal = substruct3->championRibbon;
            break;
        case MON_DATA_WINNING_RIBBON:
            retVal = substruct3->winningRibbon;
            break;
        case MON_DATA_VICTORY_RIBBON:
            retVal = substruct3->victoryRibbon;
            break;
        case MON_DATA_ARTIST_RIBBON:
            retVal = substruct3->artistRibbon;
            break;
        case MON_DATA_EFFORT_RIBBON:
            retVal = substruct3->effortRibbon;
            break;
        case MON_DATA_GIFT_RIBBON_1:
            retVal = substruct3->giftRibbon1;
            break;
        case MON_DATA_GIFT_RIBBON_2:
            retVal = substruct3->giftRibbon2;
            break;
        case MON_DATA_GIFT_RIBBON_3:
            retVal = substruct3->giftRibbon3;
            break;
        case MON_DATA_GIFT_RIBBON_4:
            retVal = substruct3->giftRibbon4;
            break;
        case MON_DATA_GIFT_RIBBON_5:
            retVal = substruct3->giftRibbon5;
            break;
        case MON_DATA_GIFT_RIBBON_6:
            retVal = substruct3->giftRibbon6;
            break;
        case MON_DATA_GIFT_RIBBON_7:
            retVal = substruct3->giftRibbon7;
            break;
        case MON_DATA_FATEFUL_ENCOUNTER:
            retVal = substruct3->fatefulEncounter;
            break;
        case MON_DATA_OBEDIENCE:
            retVal = substruct3->obedient;
            break;
        case MON_DATA_SPECIES2:
            retVal = substruct0->species;
            if (substruct0->species && (substruct3->isEgg || boxMon->isBadEgg))
                retVal = SPECIES_EGG;
            break;
        case MON_DATA_IVS:
            retVal = substruct3->hpIV | (substruct3->attackIV << 5) | (substruct3->defenseIV << 10) | (substruct3->speedIV << 15) | (substruct3->spAttackIV << 20) | (substruct3->spDefenseIV << 25);
            break;
        case MON_DATA_KNOWN_MOVES:
            if (substruct0->species && !substruct3->isEgg)
            {
                u16 *moves = (u16 *)data;
                s32 j = 0;

                while (moves[j] != MOVES_COUNT)
                {
                    u16 move = moves[j];
                    if (substruct1->moves0 == move
                        || substruct1->moves1 == move
                        || substruct1->moves2 == move
                        || substruct1->moves3 == move)
                        retVal |= gBitTable[j];
                    j++;
                }
            }
            break;
        case MON_DATA_RIBBON_COUNT:
            retVal = 0;
            if (substruct0->species && !substruct3->isEgg)
            {
                retVal += substruct3->coolRibbon;
                retVal += substruct3->beautyRibbon;
                retVal += substruct3->cuteRibbon;
                retVal += substruct3->smartRibbon;
                retVal += substruct3->toughRibbon;
                retVal += substruct3->championRibbon;
                retVal += substruct3->winningRibbon;
                retVal += substruct3->victoryRibbon;
                retVal += substruct3->artistRibbon;
                retVal += substruct3->effortRibbon;
                retVal += substruct3->giftRibbon1;
                retVal += substruct3->giftRibbon2;
                retVal += substruct3->giftRibbon3;
                retVal += substruct3->giftRibbon4;
                retVal += substruct3->giftRibbon5;
                retVal += substruct3->giftRibbon6;
                retVal += substruct3->giftRibbon7;
            }
            break;
        case MON_DATA_RIBBONS:
            retVal = 0;
            if (substruct0->species && !substruct3->isEgg)
            {
                retVal = substruct3->championRibbon
                         | (substruct3->coolRibbon << 1)
                         | (substruct3->beautyRibbon << 4)
                         | (substruct3->cuteRibbon << 7)
                         | (substruct3->smartRibbon << 10)
                         | (substruct3->toughRibbon << 13)
                         | (substruct3->winningRibbon << 16)
                         | (substruct3->victoryRibbon << 17)
                         | (substruct3->artistRibbon << 18)
                         | (substruct3->effortRibbon << 19)
                         | (substruct3->giftRibbon1 << 20)
                         | (substruct3->giftRibbon2 << 21)
                         | (substruct3->giftRibbon3 << 22)
                         | (substruct3->giftRibbon4 << 23)
                         | (substruct3->giftRibbon5 << 24)
                         | (substruct3->giftRibbon6 << 25)
                         | (substruct3->giftRibbon7 << 26);
            }
            break;
        case MON_DATA_MINTNATURE:
            retVal = substruct0->mintnature;
            break;
        case MON_DATA_SPECIES3:
    	    retVal = substruct3->isEgg ? 0:substruct0->species;
        default:
            break;
    }

//    if (field > MON_DATA_ENCRYPT_SEPARATOR)
//        EncryptBoxMon(boxMon);

    return retVal;
}

#define SET8(lhs) (lhs) = *data
#define SET16(lhs) (lhs) = data[0] + (data[1] << 8)
#define SET32(lhs) (lhs) = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24)

void SetMonData2(struct Pokemon *mon, s32 field, u32 data)
{
    SetMonData(mon, field, &data);
}

void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
{
    const u8 *data = dataArg;

    struct PokemonSubstruct0 *substruct0 = &boxMon->type0;
    struct PokemonSubstruct0 *substruct1 = substruct0;
    struct PokemonSubstruct0 *substruct2 = substruct0;
    struct PokemonSubstruct0 *substruct3 = substruct0;
    /*if (field > MON_DATA_ENCRYPT_SEPARATOR)
    {
        substruct0 = &(GetSubstruct(boxMon, boxMon->personality, 0)->type0);
        substruct1 = &(GetSubstruct(boxMon, boxMon->personality, 1)->type1);
        substruct2 = &(GetSubstruct(boxMon, boxMon->personality, 2)->type2);
        substruct3 = &(GetSubstruct(boxMon, boxMon->personality, 3)->type3);

        DecryptBoxMon(boxMon);

        if (CalculateBoxMonChecksum(boxMon) != boxMon->checksum)
        {
            boxMon->isBadEgg = 1;
            boxMon->isEgg = 1;
            substruct3->isEgg = 1;
            EncryptBoxMon(boxMon);
            return;
        }
    }*/

    switch (field)
    {
        case MON_DATA_PERSONALITY:
            SET32(boxMon->personality);
            break;
        case MON_DATA_OT_ID:
//            SET32(boxMon->otId);
            if (StringCompareN(data, gSaveBlock2Ptr->playerTrainerId, 4) != 0){//string compare is ok
                boxMon->isOtherTrainer = 1;
            }
            break;
        case MON_DATA_NICKNAME:
        {
            SetMonName(boxMon, data);
            break;
        }
        case MON_DATA_LANGUAGE:
            SET8(boxMon->language);
            break;
        case MON_DATA_SANITY_IS_BAD_EGG:
            SET8(boxMon->isBadEgg);
//            FreeMonName(boxMon);
            break;
        case MON_DATA_SANITY_HAS_SPECIES:
            SET8(boxMon->hasSpecies);
            break;
        case MON_DATA_SANITY_IS_EGG:
            SET8(boxMon->isEgg);
            break;
        case MON_DATA_OT_NAME:
        {
            /*s32 i;
            for (i = 0; i < PLAYER_NAME_LENGTH; i++)
                boxMon->otName[i] = data[i];*/
            u32 i;
            for (i = 0; data[i] != EOS; i++)
                if (data[i] != gSaveBlock2Ptr->playerName[i])
                {
                    boxMon->isOtherTrainer = 1;
                    break;
                }
            break;
        }
        case MON_DATA_MARKINGS:
            SET8(boxMon->markings);
            break;
        /*case MON_DATA_CHECKSUM:
            SET16(boxMon->checksum);
            break;*/
        case MON_DATA_ENCRYPT_SEPARATOR:
            SET16(boxMon->unknown);
            break;
        case MON_DATA_SPECIES:
        {
            SET16(substruct0->species);
            if (substruct0->species)
                boxMon->hasSpecies = 1;
            else
                boxMon->hasSpecies = 0;
            break;
        }
        case MON_DATA_HELD_ITEM:
            SET16(substruct0->heldItem);
            break;
        case MON_DATA_EXP:
            SET32(substruct0->experience);
            break;
        case MON_DATA_PP_BONUSES:
            SET8(substruct0->ppBonuses);
            break;
        case MON_DATA_FRIENDSHIP:
            SET8(substruct0->friendship);
            break;
        case MON_DATA_MOVE1:
            SET16(substruct1->moves0);
            break;
        case MON_DATA_MOVE2:
            SET16(substruct1->moves1);
            break;
        case MON_DATA_MOVE3:
            SET16(substruct1->moves2);
            break;
        case MON_DATA_MOVE4:
            SET16(substruct1->moves3);
            break;
//        case MON_DATA_PP1:
//        case MON_DATA_PP2:
//        case MON_DATA_PP3:
//        case MON_DATA_PP4:
//            SET8(substruct1->pp[field - MON_DATA_PP1]);
//            break;
        case MON_DATA_HP_EV:
            SET8(substruct2->hpEV);
            break;
        case MON_DATA_ATK_EV:
            SET8(substruct2->attackEV);
            break;
        case MON_DATA_DEF_EV:
            SET8(substruct2->defenseEV);
            break;
        case MON_DATA_SPEED_EV:
            SET8(substruct2->speedEV);
            break;
        case MON_DATA_SPATK_EV:
            SET8(substruct2->spAttackEV);
            break;
        case MON_DATA_SPDEF_EV:
            SET8(substruct2->spDefenseEV);
            break;
        case MON_DATA_COOL:
            SET8(substruct2->cool);
            break;
        case MON_DATA_BEAUTY:
            SET8(substruct2->beauty);
            break;
        case MON_DATA_CUTE:
            SET8(substruct2->cute);
            break;
        case MON_DATA_SMART:
            SET8(substruct2->smart);
            break;
        case MON_DATA_TOUGH:
            SET8(substruct2->tough);
            break;
        case MON_DATA_SHEEN:
            SET8(substruct2->sheen);
            break;
        case MON_DATA_POKERUS:
            SET8(substruct3->pokerus);
            break;
        case MON_DATA_MET_LOCATION:
            SET8(substruct3->metLocation);
            break;
        case MON_DATA_MET_LEVEL:
        {
            u8 metLevel = *data;
            substruct3->metLevel = metLevel;
            break;
        }
        case MON_DATA_MET_GAME:
            SET8(substruct3->metGame);
            break;
        case MON_DATA_POKEBALL:
        {
            u8 pokeball = *data;
            substruct0->pokeball = pokeball;
            break;
        }
        case MON_DATA_OT_GENDER:
            SET8(substruct3->otGender);
            break;
        case MON_DATA_HP_IV:
            SET8(substruct3->hpIV);
            break;
        case MON_DATA_ATK_IV:
            SET8(substruct3->attackIV);
            break;
        case MON_DATA_DEF_IV:
            SET8(substruct3->defenseIV);
            break;
        case MON_DATA_SPEED_IV:
            SET8(substruct3->speedIV);
            break;
        case MON_DATA_SPATK_IV:
            SET8(substruct3->spAttackIV);
            break;
        case MON_DATA_SPDEF_IV:
            SET8(substruct3->spDefenseIV);
            break;
        case MON_DATA_IS_EGG:
            SET8(substruct3->isEgg);
            if (substruct3->isEgg)
                boxMon->isEgg = 1;
            else
                boxMon->isEgg = 0;
            break;
        case MON_DATA_ABILITY_NUM:
            SET8(substruct3->abilityNum);
            break;
        case MON_DATA_COOL_RIBBON:
            SET8(substruct3->coolRibbon);
            break;
        case MON_DATA_BEAUTY_RIBBON:
            SET8(substruct3->beautyRibbon);
            break;
        case MON_DATA_CUTE_RIBBON:
            SET8(substruct3->cuteRibbon);
            break;
        case MON_DATA_SMART_RIBBON:
            SET8(substruct3->smartRibbon);
            break;
        case MON_DATA_TOUGH_RIBBON:
            SET8(substruct3->toughRibbon);
            break;
        case MON_DATA_CHAMPION_RIBBON:
            SET8(substruct3->championRibbon);
            break;
        case MON_DATA_WINNING_RIBBON:
            SET8(substruct3->winningRibbon);
            break;
        case MON_DATA_VICTORY_RIBBON:
            SET8(substruct3->victoryRibbon);
            break;
        case MON_DATA_ARTIST_RIBBON:
            SET8(substruct3->artistRibbon);
            break;
        case MON_DATA_EFFORT_RIBBON:
            SET8(substruct3->effortRibbon);
            break;
        case MON_DATA_GIFT_RIBBON_1:
            SET8(substruct3->giftRibbon1);
            break;
        case MON_DATA_GIFT_RIBBON_2:
            SET8(substruct3->giftRibbon2);
            break;
        case MON_DATA_GIFT_RIBBON_3:
            SET8(substruct3->giftRibbon3);
            break;
        case MON_DATA_GIFT_RIBBON_4:
            SET8(substruct3->giftRibbon4);
            break;
        case MON_DATA_GIFT_RIBBON_5:
            SET8(substruct3->giftRibbon5);
            break;
        case MON_DATA_GIFT_RIBBON_6:
            SET8(substruct3->giftRibbon6);
            break;
        case MON_DATA_GIFT_RIBBON_7:
            SET8(substruct3->giftRibbon7);
            break;
        case MON_DATA_FATEFUL_ENCOUNTER:
            SET8(substruct3->fatefulEncounter);
            break;
        case MON_DATA_OBEDIENCE:
            SET8(substruct3->obedient);
            break;
        case MON_DATA_IVS:
        {
            u32 ivs = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
            substruct3->hpIV = ivs & 0x1F;
            substruct3->attackIV = (ivs >> 5) & 0x1F;
            substruct3->defenseIV = (ivs >> 10) & 0x1F;
            substruct3->speedIV = (ivs >> 15) & 0x1F;
            substruct3->spAttackIV = (ivs >> 20) & 0x1F;
            substruct3->spDefenseIV = (ivs >> 25) & 0x1F;
            break;
        }
        case MON_DATA_MINTNATURE:
            SET8(substruct0->mintnature);
            break;
        case MON_DATA_NAME_BIT:
            SET16(boxMon->slot);
        default:
            break;
    }

    /*if (field > MON_DATA_ENCRYPT_SEPARATOR)
    {
        boxMon->checksum = CalculateBoxMonChecksum(boxMon);
        EncryptBoxMon(boxMon);
    }*/
}

void CopyMon(void *dest, void *src, size_t size)
{
    memcpy(dest, src, size);
}

u8 GiveMonToPlayer(struct Pokemon *mon)
{
    s32 i;

    SetMonData(mon, MON_DATA_OT_NAME, gSaveBlock2Ptr->playerName);
    SetMonData(mon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender);
    SetMonData(mon, MON_DATA_OT_ID, gSaveBlock2Ptr->playerTrainerId);

    for (i = 0; i < PARTY_SIZE; i++)
    {
        if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE)
            break;
    }

    if (i >= PARTY_SIZE)
        return SendMonToPC(mon);

    CopyMon(&gPlayerParty[i], mon, sizeof(*mon));
    gPlayerPartyCount = i + 1;
    return MON_GIVEN_TO_PARTY;
}

void SendToPcCopyMon(struct BoxPokemon* checkingMon, struct Pokemon *mon)
{
    SetBoxMonData(&mon->box, MON_DATA_NICKNAME, mon->name);
    CopyMon(checkingMon, &mon->box, sizeof(mon->box));
}

u8 SendMonToPC(struct Pokemon* mon)
{
    s32 boxNo, boxPos;

    SetPCBoxToSendMon(VarGet(VAR_PC_BOX_TO_SEND_MON));

    boxNo = StorageGetCurrentBox();

    do
    {
        for (boxPos = 0; boxPos < IN_BOX_COUNT; boxPos++)
        {
            struct BoxPokemon* checkingMon = GetBoxedMonPtr(boxNo, boxPos);
            if (GetBoxMonData(checkingMon, MON_DATA_SPECIES, NULL) == SPECIES_NONE)
            {
                MonRestorePP(mon);
                SendToPcCopyMon(checkingMon, mon);
                gSpecialVar_MonBoxId = boxNo;
                gSpecialVar_MonBoxPos = boxPos;
                if (GetPCBoxToSendMon() != boxNo)
                    FlagClear(FLAG_SHOWN_BOX_WAS_FULL_MESSAGE);
                VarSet(VAR_PC_BOX_TO_SEND_MON, boxNo);
                return MON_GIVEN_TO_PC;
            }
        }

        boxNo++;
        if (boxNo == TOTAL_BOXES_COUNT)
            boxNo = 0;
    } while (boxNo != StorageGetCurrentBox());

    return MON_CANT_GIVE;
}

u8 CalculatePlayerPartyCount(void)
{
    register u8 i, slot;
    gPlayerPartyCount = 0;

    if (!gMain.inBattle)
    {
        while (gPlayerPartyCount < PARTY_SIZE
               && GetMonData(&gPlayerParty[gPlayerPartyCount], MON_DATA_SPECIES, NULL) != SPECIES_NONE)
        {
            gPlayerPartyCount++;
        }
    }
    else
    {
        for (i = 0; i < PARTY_SIZE; i++)
        {
            //slot = ((gBattlePartyCurrentOrder[i / 2] >> (!(i & 1) << 2)) << 4) >> 4;
            slot = gBattlePartyCurrentOrder[i / 2] >> (!(i & 1) << 2);
            slot <<= 4;
            slot >>= 4;
            if (GetMonData(&gPlayerParty[slot], MON_DATA_SPECIES, NULL) != SPECIES_NONE)
                gPlayerPartyCount = i + 1;
        }
    }

    return gPlayerPartyCount;
}

u8 CalculateEnemyPartyCount(void)
{
    gEnemyPartyCount = 0;

    while (gEnemyPartyCount < PARTY_SIZE
           && GetMonData(&gEnemyParty[gEnemyPartyCount], MON_DATA_SPECIES, NULL) != SPECIES_NONE)
    {
        gEnemyPartyCount++;
    }

    return gEnemyPartyCount;
}

u8 GetMonsStateToDoubles(void)
{
    s32 aliveCount = 0;
    s32 i;
    CalculatePlayerPartyCount();

    if (gPlayerPartyCount == 1)
        return gPlayerPartyCount; // PLAYER_HAS_ONE_MON

    for (i = 0; i < gPlayerPartyCount; i++)
    {
        if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2, NULL) != SPECIES_EGG
            && GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0
            && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2, NULL) != SPECIES_NONE)
            aliveCount++;
    }

    return (aliveCount > 1) ? PLAYER_HAS_TWO_USABLE_MONS : PLAYER_HAS_ONE_USABLE_MON;
}

u8 GetMonsStateToDoubles_2(void)
{
    s32 aliveCount = 0;
    s32 i;

    for (i = 0; i < PARTY_SIZE; i++)
    {
        u32 species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2, NULL);
        if (species != SPECIES_EGG && species != SPECIES_NONE
            && GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0)
            aliveCount++;
    }

    if (aliveCount == 1)
        return PLAYER_HAS_ONE_MON; // may have more than one, but only one is alive

    return (aliveCount > 1) ? PLAYER_HAS_TWO_USABLE_MONS : PLAYER_HAS_ONE_USABLE_MON;
}

u16 GetAbilityBySpecies(u16 species, u8 abilityNum)
{
    if (abilityNum == 2)
        gLastUsedAbility = gBaseStats[species].abilityHidden;
    else if (abilityNum == 1)
        gLastUsedAbility = gBaseStats[species].abilities[1];
    else
        gLastUsedAbility = gBaseStats[species].abilities[0];

    return gLastUsedAbility;
}

u16 GetMonAbility(struct Pokemon *mon)
{
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    u8 abilityNum = GetMonData(mon, MON_DATA_ABILITY_NUM, NULL);
    return GetAbilityBySpecies(species, abilityNum);
}

void CreateSecretBaseEnemyParty(struct SecretBase *secretBaseRecord)
{
    s32 i, j;

    ZeroEnemyPartyMons();
    *gBattleResources->secretBase = *secretBaseRecord;

    for (i = 0; i < PARTY_SIZE; i++)
    {
        if (gBattleResources->secretBase->party.species[i])
        {
            CreateMon(&gEnemyParty[i],
                      gBattleResources->secretBase->party.species[i],
                      gBattleResources->secretBase->party.levels[i],
                      15,
                      1,
                      gBattleResources->secretBase->party.personality[i],
                      OT_ID_RANDOM_NO_SHINY,
                      0, 0);

            SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM,
                       &gBattleResources->secretBase->party.heldItems[i]);

            for (j = 0; j < NUM_STATS; j++)
                SetMonData(&gEnemyParty[i], MON_DATA_HP_EV + j,
                           &gBattleResources->secretBase->party.EVs[i]);

            for (j = 0; j < MAX_MON_MOVES; j++)
            {
                SetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j,
                           &gBattleResources->secretBase->party.moves[i * MAX_MON_MOVES + j]);
                SetMonData(&gEnemyParty[i], MON_DATA_PP1 + j,
                           &gBattleMoves[gBattleResources->secretBase->party.moves[i * MAX_MON_MOVES + j]].pp);
            }
        }
    }
}

u8 GetSecretBaseTrainerPicIndex(void)
{
    u8 facilityClass = sSecretBaseFacilityClasses[gBattleResources->secretBase->gender][gBattleResources->secretBase->trainerId[0] % 5];
    return gFacilityClassToPicIndex[facilityClass];
}

u8 GetSecretBaseTrainerClass(void)
{
    u8 facilityClass = sSecretBaseFacilityClasses[gBattleResources->secretBase->gender][gBattleResources->secretBase->trainerId[0] % 5];
    return gFacilityClassToTrainerClass[facilityClass];
}

bool8 IsPlayerPartyAndPokemonStorageFull(void)
{
    s32 i;

    for (i = 0; i < PARTY_SIZE; i++)
        if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL) == SPECIES_NONE)
            return FALSE;

    return IsPokemonStorageFull();
}

bool8 IsPokemonStorageFull(void)
{
    s32 i, j;

    for (i = 0; i < TOTAL_BOXES_COUNT; i++)
        for (j = 0; j < IN_BOX_COUNT; j++)
            if (GetBoxMonDataAt(i, j, MON_DATA_SPECIES) == SPECIES_NONE)
                return FALSE;

    return TRUE;
}

void GetSpeciesName(u8 *name, u16 species)
{
    s32 i;

    for (i = 0; i <= POKEMON_NAME_LENGTH; i++)
    {
        if (species > NUM_SPECIES)
            name[i] = gSpeciesNames[0][i];
        else
            name[i] = gSpeciesNames[species][i];

        if (name[i] == EOS)
            break;
    }

    name[i] = EOS;
}

u8 CalculatePPWithBonus(u16 move, u8 ppBonuses, u8 moveIndex)
{
    u8 basePP = gBattleMoves[move].pp;
    return basePP + ((basePP * 20 * ((gPPUpGetMask[moveIndex] & ppBonuses) >> (2 * moveIndex))) / 100);
}

void RemoveMonPPBonus(struct Pokemon *mon, u8 moveIndex)
{
    u8 ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
    ppBonuses &= gPPUpSetMask[moveIndex];
    SetMonData(mon, MON_DATA_PP_BONUSES, &ppBonuses);
}

void RemoveBattleMonPPBonus(struct BattlePokemon *mon, u8 moveIndex)
{
    mon->ppBonuses &= gPPUpSetMask[moveIndex];
}

void PokemonToBattleMon(struct Pokemon *src, struct BattlePokemon *dst)
{
    s32 i;
    u8 nickname[POKEMON_NAME_LENGTH * 2];

    for (i = 0; i < MAX_MON_MOVES; i++)
    {
        dst->moves[i] = GetMonData(src, MON_DATA_MOVE1 + i, NULL);
        dst->pp[i] = GetMonData(src, MON_DATA_PP1 + i, NULL);
    }

    dst->species = GetMonData(src, MON_DATA_SPECIES, NULL);
    dst->item = GetMonData(src, MON_DATA_HELD_ITEM, NULL);
    dst->ppBonuses = GetMonData(src, MON_DATA_PP_BONUSES, NULL);
    dst->friendship = GetMonData(src, MON_DATA_FRIENDSHIP, NULL);
    dst->experience = GetMonData(src, MON_DATA_EXP, NULL);
    dst->hpIV = GetMonData(src, MON_DATA_HP_IV, NULL);
    dst->attackIV = GetMonData(src, MON_DATA_ATK_IV, NULL);
    dst->defenseIV = GetMonData(src, MON_DATA_DEF_IV, NULL);
    dst->speedIV = GetMonData(src, MON_DATA_SPEED_IV, NULL);
    dst->spAttackIV = GetMonData(src, MON_DATA_SPATK_IV, NULL);
    dst->spDefenseIV = GetMonData(src, MON_DATA_SPDEF_IV, NULL);
    dst->personality = GetMonData(src, MON_DATA_PERSONALITY, NULL);
    dst->status1 = GetMonData(src, MON_DATA_STATUS, NULL);
    dst->level = GetMonData(src, MON_DATA_LEVEL, NULL);
    dst->hp = GetMonData(src, MON_DATA_HP, NULL);
    dst->maxHP = GetMonData(src, MON_DATA_MAX_HP, NULL);
    dst->attack = GetMonData(src, MON_DATA_ATK, NULL);
    dst->defense = GetMonData(src, MON_DATA_DEF, NULL);
    dst->speed = GetMonData(src, MON_DATA_SPEED, NULL);
    dst->spAttack = GetMonData(src, MON_DATA_SPATK, NULL);
    dst->spDefense = GetMonData(src, MON_DATA_SPDEF, NULL);
//    dst->abilityNum = GetMonData(src, MON_DATA_ABILITY_NUM, NULL);
    dst->otId = GetMonData(src, MON_DATA_OT_ID, NULL);
    dst->type1 = gBaseStats[dst->species].type1;
    dst->type2 = gBaseStats[dst->species].type2;
    dst->type3 = TYPE_MYSTERY;
    dst->ability = GetMonAbility(src);
    GetMonData(src, MON_DATA_NICKNAME, nickname);
    StringCopy10(dst->nickname, nickname);
    GetMonData(src, MON_DATA_OT_NAME, dst->otName);

    for (i = 0; i < NUM_BATTLE_STATS; i++)
        dst->statStages[i] = 6;

    dst->status2 = 0;
}

void CopyPlayerPartyMonToBattleData(u8 battlerId, u8 partyIndex)
{
    PokemonToBattleMon(&gPlayerParty[partyIndex], &gBattleMons[battlerId]);
    gBattleStruct->hpOnSwitchout[GetBattlerSide(battlerId)] = gBattleMons[battlerId].hp;
    UpdateSentPokesToOpponentValue(battlerId);
    ClearTemporarySpeciesSpriteData(battlerId, FALSE);
}

bool8 ExecuteTableBasedItemEffect(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex)
{
    return PokemonUseItemEffects(mon, item, partyIndex, moveIndex, 0);
}

bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex, u8 e)
{
    u32 dataUnsigned;
    s32 dataSigned, evCap;
    s32 friendship;
    s32 cmdIndex;
    bool8 retVal = TRUE;
    const u8 *itemEffect;
    u8 itemEffectParam = 6;
    u32 var_38;
    s8 friendshipChange = 0;
    u8 holdEffect;
    u8 battlerId = 4;
    u32 friendshipOnly = 0;
    u16 heldItem;
    u8 effectFlags;
    u32 r4;
    u32 r5;
    s8 evChange;
    u16 evCount;

    heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
    if (heldItem == ITEM_ENIGMA_BERRY)
    {
        if (gMain.inBattle)
            holdEffect = gEnigmaBerries[gBattlerInMenuId].holdEffect;
        else
            holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
    }
    else
    {
        holdEffect = ItemId_GetHoldEffect(heldItem);
    }

    gPotentialItemEffectBattler = gBattlerInMenuId;
    if (gMain.inBattle)
    {
        gActiveBattler = gBattlerInMenuId;
        cmdIndex = (GetBattlerSide(gActiveBattler) != B_SIDE_PLAYER);
        while (cmdIndex < gBattlersCount)
        {
            if (gBattlerPartyIndexes[cmdIndex] == partyIndex)
            {
                battlerId = cmdIndex;
                break;
            }
            cmdIndex += 2;
        }
    }
    else
    {
        gActiveBattler = 0;
        battlerId = MAX_BATTLERS_COUNT;
    }

//    if (!ITEM_HAS_EFFECT(item))
//        return TRUE;

    if (gItemEffectTable[item - ITEM_POTION] == NULL && item != ITEM_ENIGMA_BERRY)
        return TRUE;

    if (item == ITEM_ENIGMA_BERRY)
    {
        if (gMain.inBattle)
            itemEffect = gEnigmaBerries[gActiveBattler].itemEffect;
        else
            itemEffect = gSaveBlock1Ptr->enigmaBerry.itemEffect;
    }
    else
    {
        if (ITEM_SWEET_APPLE == item)
            itemEffect = gItemEffect_EvoStone;
        else
            itemEffect = gItemEffectTable[item - ITEM_POTION];
    }

    for (cmdIndex = 0; cmdIndex < 6; cmdIndex++)
    {
        switch (cmdIndex)
        {
            // infatuation heal, x attack, sacred ash and dire hit
            case 0:
                if ((itemEffect[cmdIndex] & ITEM0_INFATUATION)
                    && gMain.inBattle && battlerId != MAX_BATTLERS_COUNT && (gBattleMons[battlerId].status2 & STATUS2_INFATUATION))
                {
                    gBattleMons[battlerId].status2 &= ~STATUS2_INFATUATION;
                    retVal = FALSE;
                }
                if ((itemEffect[cmdIndex] & ITEM0_DIRE_HIT)
                    && !(gBattleMons[gActiveBattler].status2 & STATUS2_FOCUS_ENERGY))
                {
                    gBattleMons[gActiveBattler].status2 |= STATUS2_FOCUS_ENERGY;
                    retVal = FALSE;
                }
                if ((itemEffect[cmdIndex] & ITEM0_X_ATTACK)
                    && gBattleMons[gActiveBattler].statStages[STAT_ATK] < MAX_STAT_STAGE)
                {
                    gBattleMons[gActiveBattler].statStages[STAT_ATK] += itemEffect[cmdIndex] & ITEM0_X_ATTACK;
                    if (gBattleMons[gActiveBattler].statStages[STAT_ATK] > MAX_STAT_STAGE)
                        gBattleMons[gActiveBattler].statStages[STAT_ATK] = MAX_STAT_STAGE;
                    retVal = FALSE;
                }
                break;
                // in-battle stat boosting effects
            case 1:
                if ((itemEffect[cmdIndex] & ITEM1_X_DEFEND)
                    && gBattleMons[gActiveBattler].statStages[STAT_DEF] < MAX_STAT_STAGE)
                {
                    gBattleMons[gActiveBattler].statStages[STAT_DEF] += (itemEffect[cmdIndex] & ITEM1_X_DEFEND) >> 4;
                    if (gBattleMons[gActiveBattler].statStages[STAT_DEF] > MAX_STAT_STAGE)
                        gBattleMons[gActiveBattler].statStages[STAT_DEF] = MAX_STAT_STAGE;
                    retVal = FALSE;
                }
                if ((itemEffect[cmdIndex] & ITEM1_X_SPEED)
                    && gBattleMons[gActiveBattler].statStages[STAT_SPEED] < MAX_STAT_STAGE)
                {
                    gBattleMons[gActiveBattler].statStages[STAT_SPEED] += itemEffect[cmdIndex] & ITEM1_X_SPEED;
                    if (gBattleMons[gActiveBattler].statStages[STAT_SPEED] > MAX_STAT_STAGE)
                        gBattleMons[gActiveBattler].statStages[STAT_SPEED] = MAX_STAT_STAGE;
                    retVal = FALSE;
                }
                break;
                // more stat boosting effects
            case 2:
                if ((itemEffect[cmdIndex] & ITEM2_X_ACCURACY)
                    && gBattleMons[gActiveBattler].statStages[STAT_ACC] < MAX_STAT_STAGE)
                {
                    gBattleMons[gActiveBattler].statStages[STAT_ACC] += (itemEffect[cmdIndex] & ITEM2_X_ACCURACY) >> 4;
                    if (gBattleMons[gActiveBattler].statStages[STAT_ACC] > MAX_STAT_STAGE)
                        gBattleMons[gActiveBattler].statStages[STAT_ACC] = MAX_STAT_STAGE;
                    retVal = FALSE;
                }
                if ((itemEffect[cmdIndex] & ITEM2_X_SPATK)
                    && gBattleMons[gActiveBattler].statStages[STAT_SPATK] < MAX_STAT_STAGE)
                {
                    gBattleMons[gActiveBattler].statStages[STAT_SPATK] += itemEffect[cmdIndex] & ITEM2_X_SPATK;
                    if (gBattleMons[gActiveBattler].statStages[STAT_SPATK] > MAX_STAT_STAGE)
                        gBattleMons[gActiveBattler].statStages[STAT_SPATK] = MAX_STAT_STAGE;
                    retVal = FALSE;
                }
                break;
            case 3:
                if ((itemEffect[cmdIndex] & ITEM3_GUARD_SPEC)
                    && gSideTimers[GetBattlerSide(gActiveBattler)].mistTimer == 0)
                {
                    gSideTimers[GetBattlerSide(gActiveBattler)].mistTimer = 5;
                    retVal = FALSE;
                }
                if ((itemEffect[cmdIndex] & ITEM3_LEVEL_UP)
                    && GetMonData(mon, MON_DATA_LEVEL, NULL) != VarGet(VAR_MAX_LEVEL))
                {
                    dataUnsigned = gExperienceTables[gBaseStats[GetMonData(mon, MON_DATA_SPECIES, NULL)].growthRate][GetMonData(mon, MON_DATA_LEVEL, NULL) + gSpecialVar_0x8004];
                    SetMonData(mon, MON_DATA_EXP, &dataUnsigned);
                    CalculateMonStats(mon);
                    retVal = FALSE;
                }
                if ((itemEffect[cmdIndex] & ITEM3_SLEEP)
                    && HealStatusConditions(mon, partyIndex, 7, battlerId) == 0)
                {
                    if (battlerId != 4)
                        gBattleMons[battlerId].status2 &= ~STATUS2_NIGHTMARE;
                    retVal = FALSE;
                }
                if ((itemEffect[cmdIndex] & ITEM3_POISON) && HealStatusConditions(mon, partyIndex, STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER, battlerId) == 0)
                    retVal = FALSE;
                if ((itemEffect[cmdIndex] & ITEM3_BURN) && HealStatusConditions(mon, partyIndex, STATUS1_BURN, battlerId) == 0)
                    retVal = FALSE;
                if ((itemEffect[cmdIndex] & ITEM3_FREEZE) && HealStatusConditions(mon, partyIndex, STATUS1_FREEZE, battlerId) == 0)
                    retVal = FALSE;
                if ((itemEffect[cmdIndex] & ITEM3_PARALYSIS) && HealStatusConditions(mon, partyIndex, STATUS1_PARALYSIS, battlerId) == 0)
                    retVal = FALSE;
                if ((itemEffect[cmdIndex] & ITEM3_CONFUSION)  // heal confusion
                    && gMain.inBattle && battlerId != MAX_BATTLERS_COUNT && (gBattleMons[battlerId].status2 & STATUS2_CONFUSION))
                {
                    gBattleMons[battlerId].status2 &= ~STATUS2_CONFUSION;
                    retVal = FALSE;
                }
                break;
                // EV, HP, and PP raising effects
            case 4:
                effectFlags = itemEffect[cmdIndex];
                if (effectFlags & ITEM4_PP_UP)
                {
                    effectFlags &= ~ITEM4_PP_UP;
                    dataUnsigned = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
                    var_38 = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
                    if (dataUnsigned <= 2 && var_38 > 4)
                    {
                        dataUnsigned = GetMonData(mon, MON_DATA_PP_BONUSES, NULL) + gPPUpAddMask[moveIndex];
                        SetMonData(mon, MON_DATA_PP_BONUSES, &dataUnsigned);

                    dataUnsigned = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), dataUnsigned, moveIndex) - var_38;
                    dataUnsigned = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL) + dataUnsigned;
                        SetMonData(mon, MON_DATA_PP1 + moveIndex, &dataUnsigned);
                    retVal = FALSE;
                }
            }
            var_38 = 0;
            while (effectFlags != 0)
            {
                if (effectFlags & 1)
                {
                    switch (var_38)
                    {
                    case 0:
                    case 1:
                        // ev raise
                        evCount = GetMonEVCount(mon);
                        r5 = itemEffect[itemEffectParam];
                        dataSigned = GetMonData(mon, sGetMonDataEVConstants[var_38], NULL);
                        evChange = r5;
                        if (evChange > 0)
                        {
                            if (evCount >= MAX_TOTAL_EVS)
                                return TRUE;

                            if (itemEffect[10] & ITEM10_IS_VITAMIN)
                                evCap = EV_ITEM_RAISE_LIMIT;
                            else
                                evCap = 252;

                            if (dataSigned >= evCap)
                                break;

                            if (dataSigned + evChange > evCap)
                                r5 = evCap - (dataSigned + evChange) + evChange;
                            else
                                r5 = evChange;

                            if (evCount + r5 > MAX_TOTAL_EVS)
                                r5 += MAX_TOTAL_EVS - (evCount + r5);
                            dataSigned += r5;
                        }
                        else
                        {
                            if (dataSigned == 0)
                            {
                                friendshipOnly = 1;
                                itemEffectParam++;
                                break;
                            }
                            dataSigned += evChange;
                            if (dataSigned < 0)
                                dataSigned = 0;
                        }
                            SetMonData(mon, sGetMonDataEVConstants[var_38], &dataSigned);
                        CalculateMonStats(mon);
                        itemEffectParam++;
                        retVal = FALSE;
                        break;
                    case 2:
                        // revive
                        if (effectFlags & 0x10)
                        {
                            if (GetMonData(mon, MON_DATA_HP, NULL) != 0)
                            {
                                itemEffectParam++;
                                break;
                            }
                            if (gMain.inBattle)
                            {
                                if (battlerId != 4)
                                {
                                    gAbsentBattlerFlags &= ~gBitTable[battlerId];
                                    CopyPlayerPartyMonToBattleData(battlerId, GetPartyIdFromBattlePartyId(gBattlerPartyIndexes[battlerId]));
                                    if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER && gBattleResults.numRevivesUsed < 255)
                                        gBattleResults.numRevivesUsed++;
                                }
                                else
                                {
                                    gAbsentBattlerFlags &= ~gBitTable[gActiveBattler ^ 2];
                                    if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER && gBattleResults.numRevivesUsed < 255)
                                        gBattleResults.numRevivesUsed++;
                                }
                            }
                        }
                        else
                        {
                            if (GetMonData(mon, MON_DATA_HP, NULL) == 0)
                            {
                                itemEffectParam++;
                                break;
                            }
                        }
                        dataUnsigned = itemEffect[itemEffectParam++];
                        switch (dataUnsigned)
                        {
                        case 0xFF:
                            dataUnsigned = GetMonData(mon, MON_DATA_MAX_HP, NULL) - GetMonData(mon, MON_DATA_HP, NULL);
                            break;
                        case 0xFE:
                            dataUnsigned = GetMonData(mon, MON_DATA_MAX_HP, NULL) / 2;
                            if (dataUnsigned == 0)
                                dataUnsigned = 1;
                            break;
                        case 0xFD:
                            dataUnsigned = gBattleScripting.levelUpHP;
                            break;
                        }
                        if (GetMonData(mon, MON_DATA_MAX_HP, NULL) != GetMonData(mon, MON_DATA_HP, NULL))
                        {
                            if (e == 0)
                            {
                                dataUnsigned = GetMonData(mon, MON_DATA_HP, NULL) + dataUnsigned;
                                if (dataUnsigned > GetMonData(mon, MON_DATA_MAX_HP, NULL))
                                    dataUnsigned = GetMonData(mon, MON_DATA_MAX_HP, NULL);
                                SetMonData(mon, MON_DATA_HP, &dataUnsigned);
                                if (gMain.inBattle && battlerId != 4)
                                {
                                    gBattleMons[battlerId].hp = dataUnsigned;
                                    if (!(effectFlags & 0x10) && GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
                                    {
                                        if (gBattleResults.numHealingItemsUsed < 255)
                                            gBattleResults.numHealingItemsUsed++;
                                        // I have to re-use this variable to match.
                                        r5 = gActiveBattler;
                                        gActiveBattler = battlerId;
                                        BtlController_EmitGetMonData(0, REQUEST_ALL_BATTLE, 0);
                                        MarkBattlerForControllerExec(gActiveBattler);
                                        gActiveBattler = r5;
                                    }
                                }
                            }
                            else
                            {
                                gBattleMoveDamage = -dataUnsigned;
                            }
                            retVal = FALSE;
                        }
                        effectFlags &= 0xEF;
                        break;
                    case 3:
                        // Heal pp in all moves.
                        if (!(effectFlags & 2))
                        {
                            for (r5 = 0; (signed)(r5) < (signed)(4); r5++)
                            {
                                u16 moveId;

                                        dataUnsigned = GetMonData(mon, MON_DATA_PP1 + r5, NULL);
                                        moveId = GetMonData(mon, MON_DATA_MOVE1 + r5, NULL);
                                        if (dataUnsigned != CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), r5))
                                        {
                                            dataUnsigned += itemEffect[itemEffectParam];
                                            moveId = GetMonData(mon, MON_DATA_MOVE1 + r5, NULL);
                                            if (dataUnsigned > CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), r5))
                                            {
                                                moveId = GetMonData(mon, MON_DATA_MOVE1 + r5, NULL);
                                                dataUnsigned = CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), r5);
                                            }
                                            SetMonData(mon, MON_DATA_PP1 + r5, &dataUnsigned);
                                            if (gMain.inBattle
                                                && battlerId != MAX_BATTLERS_COUNT && !(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)
                                                && !(gDisableStructs[battlerId].mimickedMoves & gBitTable[r5]))
                                                gBattleMons[battlerId].pp[r5] = dataUnsigned;
                                            retVal = FALSE;
                                        }
                                    }
                                    itemEffectParam++;
                                }
                                    // Heal pp in one move.
                                else
                                {
                                    u16 moveId;

                                    dataUnsigned = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL);
                                    moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
                                    if (dataUnsigned != CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex))
                                    {
                                        dataUnsigned += itemEffect[itemEffectParam++];
                                        moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
                                        if (dataUnsigned > CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex))
                                        {
                                            moveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
                                            dataUnsigned = CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
                                        }
                                        SetMonData(mon, MON_DATA_PP1 + moveIndex, &dataUnsigned);
                                        if (gMain.inBattle
                                            && battlerId != 4 && !(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)
                                            && !(gDisableStructs[battlerId].mimickedMoves & gBitTable[moveIndex]))
                                            gBattleMons[battlerId].pp[moveIndex] = dataUnsigned;
                                        retVal = FALSE;
                                    }
                                }
                                break;
                                // Evolution stone
                            case 7:
                            {
                                u16 targetSpecies = GetEvolutionTargetSpecies(mon, 2, item);

                            if (targetSpecies != SPECIES_NONE)
                            {
                                BeginEvolutionScene(mon, targetSpecies, 0, partyIndex);
                                return FALSE;
                            }
                        }
                        break;
                    }
                }
                var_38++;
                effectFlags >>= 1;
            }
            break;
        // EV and friendship
        case 5:
            effectFlags = itemEffect[cmdIndex];
            var_38 = 0;
            while (effectFlags != 0)
            {
                if (effectFlags & 1)
                {
                    switch (var_38)
                    {
                    case 0:
                    case 1:
                    case 2:
                    case 3:
                        evCount = GetMonEVCount(mon);
                        r5 = itemEffect[itemEffectParam];
                        dataSigned = GetMonData(mon, sGetMonDataEVConstants[var_38 + 2], NULL);
                        evChange = r5;
                        if (evChange > 0)
                        {
                            if (evCount >= MAX_TOTAL_EVS)
                                return TRUE;

                            if (itemEffect[10] & ITEM10_IS_VITAMIN)
                                evCap = EV_ITEM_RAISE_LIMIT;
                            else
                                evCap = 252;

                            if (dataSigned >= evCap)
                                break;

                            if (dataSigned + evChange > evCap)
                                r5 = evCap - (dataSigned + evChange) + evChange;
                            else
                                r5 = evChange;

                                    if (evCount + r5 > MAX_TOTAL_EVS)
                                        r5 += MAX_TOTAL_EVS - (evCount + r5);
                                    dataSigned += r5;
                                }
                                else
                                {
                                    if (dataSigned == 0)
                                    {
                                        friendshipOnly = 1;
                                        itemEffectParam++;
                                        break;
                                    }
                                    dataSigned += evChange;
                                    if (dataSigned < 0)
                                        dataSigned = 0;
                                }
                            SetMonData(mon, sGetMonDataEVConstants[var_38 + 2], &dataSigned);
                                CalculateMonStats(mon);
                                retVal = FALSE;
                                itemEffectParam++;
                                break;
                            case 4:
                                dataUnsigned = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
                                r5 = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
                                if (dataUnsigned < 3 && r5 > 4)
                                {
                                    dataUnsigned = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
                                    dataUnsigned &= gPPUpSetMask[moveIndex];
                                    dataUnsigned += gPPUpAddMask[moveIndex] * 3;

                                    SetMonData(mon, MON_DATA_PP_BONUSES, &dataUnsigned);
                                    dataUnsigned = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), dataUnsigned, moveIndex) - r5;
                                    dataUnsigned = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL) + dataUnsigned;
                                    SetMonData(mon, MON_DATA_PP1 + moveIndex, &dataUnsigned);
                                    retVal = FALSE;
                                }
                                break;
                            case 5:
                                if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 100 && (retVal == 0 || friendshipOnly != 0) && !ShouldSkipFriendshipChange() && friendshipChange == 0)
                                {
                                    friendshipChange = itemEffect[itemEffectParam];
                                    friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
                                    if (friendshipChange > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
                                        friendship += 150 * friendshipChange / 100;
                                    else
                                        friendship += friendshipChange;
                                    if (friendshipChange > 0)
                                    {
                                        if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL)
                                            friendship++;
                                        if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
                                            friendship++;
                                    }
                                    if (friendship < 0)
                                        friendship = 0;
                                    if (friendship > MAX_FRIENDSHIP)
                                        friendship = MAX_FRIENDSHIP;
                                    SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
                                    retVal = FALSE;
                                }
                                itemEffectParam++;
                                break;
                            case 6:
                                if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 100 && GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 200
                                    && (retVal == 0 || friendshipOnly != 0) && !ShouldSkipFriendshipChange() && friendshipChange == 0)
                                {
                                    friendshipChange = itemEffect[itemEffectParam];
                                    friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
                                    if ((s8)(friendshipChange) > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
                                        friendship += 150 * friendshipChange / 100;
                                    else
                                        friendship += friendshipChange;
                                    if (friendshipChange > 0)
                                    {
                                        if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL)
                                            friendship++;
                                        if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
                                            friendship++;
                                    }
                                    if (friendship < 0)
                                        friendship = 0;
                                    if (friendship > MAX_FRIENDSHIP)
                                        friendship = MAX_FRIENDSHIP;
                                    SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
                                    retVal = FALSE;
                                }
                                itemEffectParam++;
                                break;
                            case 7:
                                if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 200 && (retVal == 0 || friendshipOnly != 0) && !ShouldSkipFriendshipChange() && friendshipChange == 0)
                                {
                                    friendshipChange = itemEffect[itemEffectParam];
                                    friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
                                    if ((s8)(friendshipChange) > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
                                        friendship += 150 * friendshipChange / 100;
                                    else
                                        friendship += friendshipChange;
                                    if (friendshipChange > 0)
                                    {
                                        if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL)
                                            friendship++;
                                        if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
                                            friendship++;
                                    }
                                    if (friendship < 0)
                                        friendship = 0;
                                    if (friendship > MAX_FRIENDSHIP)
                                        friendship = MAX_FRIENDSHIP;
                                    SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
                                    retVal = FALSE;
                                }
                                itemEffectParam++;
                                break;
                        }
                    }
                    var_38++;
                effectFlags >>= 1;
                }
                break;
        }
    }
    return retVal;
}

bool8 HealStatusConditions(struct Pokemon *mon, u32 battlePartyId, u32 healMask, u8 battlerId)
{
    u32 status = GetMonData(mon, MON_DATA_STATUS, 0);

    if (status & healMask)
    {
        status &= ~healMask;
        SetMonData(mon, MON_DATA_STATUS, &status);
        if (gMain.inBattle && battlerId != MAX_BATTLERS_COUNT)
            gBattleMons[battlerId].status1 &= ~healMask;
        return FALSE;
    }
    else
    {
        return TRUE;
    }
}

u8 GetItemEffectParamOffset(u16 itemId, u8 effectByte, u8 effectBit)
{
    const u8 *temp;
    const u8 *itemEffect;
    u8 offset;
    int i;
    u8 j;
    u8 val;

    offset = 6;

    temp = gItemEffectTable[itemId - ITEM_POTION];

    if (!temp && itemId != ITEM_ENIGMA_BERRY)
        return 0;

    if (itemId == ITEM_ENIGMA_BERRY)
    {
        temp = gEnigmaBerries[gActiveBattler].itemEffect;
    }

    itemEffect = temp;

    for (i = 0; i < 6; i++)
    {
        switch (i)
        {
            case 0:
            case 1:
            case 2:
            case 3:
                if (i == effectByte)
                    return 0;
                break;
            case 4:
                val = itemEffect[4];
                if (val & ITEM4_PP_UP)
                    val &= ~(ITEM4_PP_UP);
                j = 0;
                while (val)
                {
                    if (val & 1)
                    {
                        switch (j)
                        {
                            case 2:
                                if (val & 0x10)
                                    val &= 0xEF;
                            case 0:
                                if (i == effectByte && (val & effectBit))
                                    return offset;
                                offset++;
                                break;
                            case 1:
                                if (i == effectByte && (val & effectBit))
                                    return offset;
                                offset++;
                                break;
                            case 3:
                                if (i == effectByte && (val & effectBit))
                                    return offset;
                                offset++;
                                break;
                            case 7:
                                if (i == effectByte)
                                    return 0;
                                break;
                        }
                    }
                    j++;
                    val >>= 1;
                    if (i == effectByte)
                        effectBit >>= 1;
                }
                break;
            case 5:
                val = itemEffect[5];
                j = 0;
                while (val)
                {
                    if (val & 1)
                    {
                        switch (j)
                        {
                            case 0:
                            case 1:
                            case 2:
                            case 3:
                            case 4:
                            case 5:
                            case 6:
                                if (i == effectByte && (val & effectBit))
                                    return offset;
                                offset++;
                                break;
                            case 7:
                                if (i == effectByte)
                                    return 0;
                                break;
                        }
                    }
                    j++;
                    val >>= 1;
                    if (i == effectByte)
                        effectBit >>= 1;
                }
                break;
        }
    }

    return offset;
}

static void BufferStatRoseMessage(s32 arg0)
{
    gBattlerTarget = gBattlerInMenuId;
    StringCopy(gBattleTextBuff1, gStatNamesTable[sStatsToRaise[arg0]]);
    StringCopy(gBattleTextBuff2, gText_StatRose);
    BattleStringExpandPlaceholdersToDisplayedString(gText_PkmnsStatChanged2);
}

u8 *UseStatIncreaseItem(u16 itemId)
{
    int i;
    const u8 *itemEffect;

    if (itemId == ITEM_ENIGMA_BERRY)
    {
        if (gMain.inBattle)
            itemEffect = gEnigmaBerries[gBattlerInMenuId].itemEffect;
        else
            itemEffect = gSaveBlock1Ptr->enigmaBerry.itemEffect;
    }
    else
    {
        itemEffect = gItemEffectTable[itemId - ITEM_POTION];
    }

    gPotentialItemEffectBattler = gBattlerInMenuId;

    for (i = 0; i < 3; i++)
    {
        if (itemEffect[i] & (ITEM0_X_ATTACK | ITEM1_X_SPEED | ITEM2_X_SPATK))
            BufferStatRoseMessage(i * 2);

        if (itemEffect[i] & (ITEM0_DIRE_HIT | ITEM1_X_DEFEND | ITEM2_X_ACCURACY))
        {
            if (i != 0) // Dire Hit is the only ITEM0 above
            {
                BufferStatRoseMessage(i * 2 + 1);
            }
            else
            {
                gBattlerAttacker = gBattlerInMenuId;
                BattleStringExpandPlaceholdersToDisplayedString(gText_PkmnGettingPumped);
            }
        }
    }

    if (itemEffect[3] & ITEM3_GUARD_SPEC)
    {
        gBattlerAttacker = gBattlerInMenuId;
        BattleStringExpandPlaceholdersToDisplayedString(gText_PkmnShroudedInMist);
    }

    return gDisplayedStringBattle;
}

u8 GetNature(struct Pokemon *mon)
{
    return GetMonData(mon, MON_DATA_PERSONALITY, 0) % NUM_NATURES;
}

u8 GetNatureFromPersonality(u32 personality)
{
    return personality % NUM_NATURES;
}

u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 type, u16 evolutionItem)
{
    int i, j;
    u16 targetSpecies = 0;
    u16 heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0);
    u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0);
    u8 level;
    u16 friendship;
    u8 beauty = GetMonData(mon, MON_DATA_BEAUTY, 0);
    u16 upperPersonality = personality >> 16;
    u8 holdEffect;
    const struct Evolution *evolution;
    u16 evos_per_mon;
    if (heldItem == ITEM_ENIGMA_BERRY)
        holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
    else
        holdEffect = ItemId_GetHoldEffect(heldItem);

    if (holdEffect == HOLD_EFFECT_PREVENT_EVOLVE && type != 3)
        return SPECIES_NONE;

    evos_per_mon = GetMonData(mon, MON_DATA_SPECIES, 0);
    if (evos_per_mon == SPECIES_EEVEE)
    {
        evolution = gEveEvolutionTable;
        evos_per_mon = ARRAY_COUNT(gEveEvolutionTable);
    } else
    {
        evolution = &gEvolutionTable[evos_per_mon][0];
        evos_per_mon = EVOS_PER_MON;
    }
    switch (type)
    {
    case 0:
        level = GetMonData(mon, MON_DATA_LEVEL, 0);
        friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, 0);

        for (i = 0; i < evos_per_mon; i++)
        {
            switch (evolution[i].method)
            {
            case EVO_FRIENDSHIP:
                if (friendship >= 220)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_FRIENDSHIP_DAY:
                RtcCalcLocalTime();
                if (IsCurrentlyDay() && friendship >= 220)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL_DAY:
                RtcCalcLocalTime();
                if (IsCurrentlyDay() && evolution[i].param <= level)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_FRIENDSHIP_NIGHT:
                RtcCalcLocalTime();
                if (!IsCurrentlyDay() && friendship >= 220)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL_NIGHT:
                RtcCalcLocalTime();
                if (!IsCurrentlyDay() && evolution[i].param <= level)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_ITEM_HOLD_NIGHT:
                RtcCalcLocalTime();
                if (!IsCurrentlyDay() && heldItem == evolution[i].param)
                {
                    heldItem = 0;
                    SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem);
                    targetSpecies = evolution[i].targetSpecies;
                }
                break;
            case EVO_ITEM_HOLD_DAY:
                RtcCalcLocalTime();
                if (IsCurrentlyDay() && heldItem == evolution[i].param)
                {
                    heldItem = 0;
                    SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem);
                    targetSpecies = evolution[i].targetSpecies;
                }
                break;
            case EVO_LEVEL_DUSK:
                RtcCalcLocalTime();
                if (gLocalTime.hours >= 17 && gLocalTime.hours < 20 && evolution[i].param <= level)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL:
                if (evolution[i].param <= level)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL_FEMALE:
                if (evolution[i].param <= level && GetMonGender(mon) == MON_FEMALE)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL_MALE:
                if (evolution[i].param <= level && GetMonGender(mon) == MON_MALE)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL_ATK_GT_DEF:
                if (evolution[i].param <= level)
                    if (GetMonData(mon, MON_DATA_ATK, 0) > GetMonData(mon, MON_DATA_DEF, 0))
                        targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL_ATK_EQ_DEF:
                if (evolution[i].param <= level)
                    if (GetMonData(mon, MON_DATA_ATK, 0) == GetMonData(mon, MON_DATA_DEF, 0))
                        targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL_ATK_LT_DEF:
                if (evolution[i].param <= level)
                    if (GetMonData(mon, MON_DATA_ATK, 0) < GetMonData(mon, MON_DATA_DEF, 0))
                        targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL_SILCOON:
                if (evolution[i].param <= level && (upperPersonality % 10) <= 4)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL_CASCOON:
                if (evolution[i].param <= level && (upperPersonality % 10) > 4)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_LEVEL_NINJASK:
                if (evolution[i].param <= level)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_BEAUTY:
                if (evolution[i].param <= beauty)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_MOVE:
                if (MonKnowsMove(mon, evolution[i].param))
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_MOVE_TYPE:
                for (j = 0; j < 4; j++)
                {
                    if (gBattleMoves[GetMonData(mon, MON_DATA_MOVE1 + j, NULL)].type == evolution[i].param)
                    {
                        targetSpecies = evolution[i].targetSpecies;
                        break;
                    }
                }
                break;
            case EVO_SPECIFIC_MON_IN_PARTY:
                for (j = 0; j < PARTY_SIZE; j++)
                {
                    if (GetMonData(&gPlayerParty[j], MON_DATA_SPECIES, NULL) == evolution[i].param)
                    {
                        targetSpecies = evolution[i].targetSpecies;
                        break;
                    }
                }
                break;
            case EVO_LEVEL_DARK_TYPE_MON_IN_PARTY:
                if (evolution[i].param <= level)
                {
                    for (j = 0; j < PARTY_SIZE; j++)
                    {
                        u16 species = GetMonData(&gPlayerParty[j], MON_DATA_SPECIES, NULL);
                        if (gBaseStats[species].type1 == TYPE_DARK
                            || gBaseStats[species].type2 == TYPE_DARK)
                        {
                            targetSpecies = evolution[i].targetSpecies;
                            break;
                        }
                    }
                }
                break;
            case EVO_LEVEL_RAIN:
                j = GetCurrentWeather();
                if (j == WEATHER_RAIN || j == WEATHER_RAIN_THUNDERSTORM || j == WEATHER_DOWNPOUR)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_MAP:
                if (gMapHeader.regionMapSectionId == evolution[i].param)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_TRADE_ITEM:
                if (heldItem == evolution[i].param)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            }
        }
        break;
    case 1:
        for (i = 0; i < evos_per_mon; i++)
        {
            switch (evolution[i].method)
            {
            case EVO_TRADE:
                targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_TRADE_ITEM:
                if (evolution[i].param == heldItem)
                {
                    heldItem = 0;
                    SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem);
                    targetSpecies = evolution[i].targetSpecies;
                }
                break;
            }
        }
        break;
    case 2:
    case 3:
        for (i = 0; i < evos_per_mon; i++)
        {
            switch (evolution[i].method)
            {
            case EVO_ITEM:
                if (evolution[i].param == evolutionItem)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_ITEM_FEMALE:
                if (GetMonGender(mon) == MON_FEMALE && evolution[i].param == evolutionItem)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            case EVO_ITEM_MALE:
                if (GetMonGender(mon) == MON_MALE && evolution[i].param == evolutionItem)
                    targetSpecies = evolution[i].targetSpecies;
                break;
            }
        }
        break;
    }

    return targetSpecies;
}

u16 HoennPokedexNumToSpecies(u16 hoennNum)
{
    u16 species;

    if (!hoennNum)
        return 0;

    species = 0;

    while (species < (NUM_SPECIES - 1) && gSpeciesToHoennPokedexNum[species] != hoennNum)
        species++;

    if (species == NUM_SPECIES - 1)
        return 0;

    return species + 1;
}

u16 NationalPokedexNumToSpecies(u16 nationalNum)
{
    u16 species;

    if (!nationalNum)
        return 0;

    species = 0;

    while (species < (NUM_SPECIES - 1) && gSpeciesToNationalPokedexNum[species] != nationalNum)
        species++;

    if (species == NUM_SPECIES - 1)
        return 0;

    return species + 1;
}

u16 NationalToHoennOrder(u16 nationalNum)
{
    u16 hoennNum;

    if (!nationalNum)
        return 0;

    hoennNum = 0;

    while (hoennNum < (NUM_SPECIES - 1) && gHoennToNationalOrder[hoennNum] != nationalNum)
        hoennNum++;

    if (hoennNum == NUM_SPECIES - 1)
        return 0;

    return hoennNum + 1;
}

u16 SpeciesToNationalPokedexNum(u16 species)
{
    if (!species || species >= NUM_SPECIES)
        return 0;

    return gSpeciesToNationalPokedexNum[species - 1];
}

u16 SpeciesToHoennPokedexNum(u16 species)
{
    if (!species  || species >= NUM_SPECIES)
        return 0;

    return gSpeciesToHoennPokedexNum[species - 1];
}

u16 HoennToNationalOrder(u16 hoennNum)
{
    if (!hoennNum)
        return 0;

    return gHoennToNationalOrder[hoennNum - 1];
}


void sub_806D544(u16 species, u32 personality, u8 *dest)
{
    if (species == SPECIES_SPINDA
        && dest != gMonSpritesGfxPtr->sprites[0]
        && dest != gMonSpritesGfxPtr->sprites[2])
    {
        int i;
        for (i = 0; i < 4; i++)
        {
            int j;
            u8 x = gSpindaSpotGraphics[i].x + ((personality & 0x0F) - 8);
            u8 y = gSpindaSpotGraphics[i].y + (((personality & 0xF0) >> 4) - 8);

            for (j = 0; j < 16; j++)
            {
                int k;
                s32 row = gSpindaSpotGraphics[i].image[j];

                for (k = x; k < x + 16; k++)
                {
                    u8 *val = dest + ((k / 8) * 32) + ((k % 8) / 2) + ((y >> 3) << 8) + ((y & 7) << 2);

                    if (row & 1)
                    {
                        if (k & 1)
                        {
                            if ((u8)((*val & 0xF0) - 0x10) <= 0x20)
                                *val += 0x40;
                        }
                        else
                        {
                            if ((u8)((*val & 0xF) - 0x01) <= 0x02)
                                *val += 0x04;
                        }
                    }

                    row >>= 1;
                }

                y++;
            }

            personality >>= 8;
        }
    }
}

void DrawSpindaSpots(u16 species, u32 personality, u8 *dest, u8 a4)
{
    if (species == SPECIES_SPINDA && a4)
    {
        int i;
        for (i = 0; i < 4; i++)
        {
            int j;
            u8 x = gSpindaSpotGraphics[i].x + ((personality & 0x0F) - 8);
            u8 y = gSpindaSpotGraphics[i].y + (((personality & 0xF0) >> 4) - 8);

            for (j = 0; j < 16; j++)
            {
                int k;
                s32 row = gSpindaSpotGraphics[i].image[j];

                for (k = x; k < x + 16; k++)
                {
                    u8 *val = dest + ((k / 8) * 32) + ((k % 8) / 2) + ((y >> 3) << 8) + ((y & 7) << 2);

                    if (row & 1)
                    {
                        if (k & 1)
                        {
                            if ((u8)((*val & 0xF0) - 0x10) <= 0x20)
                                *val += 0x40;
                        }
                        else
                        {
                            if ((u8)((*val & 0xF) - 0x01) <= 0x02)
                                *val += 0x04;
                        }
                    }

                    row >>= 1;
                }

                y++;
            }

            personality >>= 8;
        }
    }
}

void EvolutionRenameMon(struct Pokemon *mon, u16 oldSpecies, u16 newSpecies)
{
    u8 language;
    GetMonData(mon, MON_DATA_NICKNAME, gStringVar1);
    language = GetMonData(mon, MON_DATA_LANGUAGE, &language);
    if (language == GAME_LANGUAGE && !StringCompare(gSpeciesNames[oldSpecies], gStringVar1))
        SetMonData(mon, MON_DATA_NICKNAME, gSpeciesNames[newSpecies]);
}

// The below two functions determine which side of a multi battle the trainer battles on
// 0 is the left (top in  party menu), 1 is right (bottom in party menu)
u8 GetPlayerFlankId(void)
{
    u8 flankId = 0;
    switch (gLinkPlayers[GetMultiplayerId()].id)
    {
        case 0:
        case 3:
            flankId = 0;
            break;
        case 1:
        case 2:
            flankId = 1;
            break;
    }
    return flankId;
}

u16 GetLinkTrainerFlankId(u8 linkPlayerId)
{
    u16 flankId = 0;
    switch (gLinkPlayers[linkPlayerId].id)
    {
        case 0:
        case 3:
            flankId = 0;
            break;
        case 1:
        case 2:
            flankId = 1;
            break;
    }
    return flankId;
}

s32 GetBattlerMultiplayerId(u16 a1)
{
    s32 id;
    for (id = 0; id < MAX_LINK_PLAYERS; id++)
        if (gLinkPlayers[id].id == a1)
            break;
    return id;
}

u8 GetTrainerEncounterMusicId(u16 trainerOpponentId)
{
    if (InBattlePyramid())
        return GetBattlePyramindTrainerEncounterMusicId(trainerOpponentId);
    else if (InTrainerHillChallenge())
        return GetTrainerEncounterMusicIdInTrainerHill(trainerOpponentId);
    else
        return TRAINER_ENCOUNTER_MUSIC(trainerOpponentId);
}

u16 ModifyStatByNature(u8 nature, u16 n, u8 statIndex)
{
    // Dont modify HP, Accuracy, or Evasion by nature
    if (statIndex <= STAT_HP || statIndex > NUM_NATURE_STATS)
    {
        // Should just be "return n", but it wouldn't match without this.
        u16 retVal = n;
        retVal++;
        retVal--;
        return retVal;
    }

    switch (gNatureStatTable[nature][statIndex - 1])
    {
        case 1:
            return (n * 110) / 100; // NOTE: will overflow for n > 595 because the intermediate value is cast to u16 before the division. Fix by removing (u16) cast
        case -1:
            return (n * 90) / 100;  // NOTE: will overflow for n > 728, see above
    }

    return n;
}

#define IS_LEAGUE_BATTLE                                                                \
    ((gBattleTypeFlags & BATTLE_TYPE_TRAINER)                                           \
    && (gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_ELITE_FOUR    \
     || gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_LEADER        \
     || gTrainers[gTrainerBattleOpponent_A].trainerClass == TRAINER_CLASS_CHAMPION))    \

void AdjustFriendship(struct Pokemon *mon, u8 event)
{
    u16 species, heldItem;
    u8 holdEffect;

    if (ShouldSkipFriendshipChange())
        return;

    species = GetMonData(mon, MON_DATA_SPECIES2, 0);
    heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0);

    if (heldItem == ITEM_ENIGMA_BERRY)
    {
        if (gMain.inBattle)
            holdEffect = gEnigmaBerries[0].holdEffect;
        else
            holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
    }
    else
    {
        holdEffect = ItemId_GetHoldEffect(heldItem);
    }

    if (species && species != SPECIES_EGG)
    {
        u8 friendshipLevel = 0;
        s16 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, 0);

        if (friendship > 99)
            friendshipLevel++;
        if (friendship > 199)
            friendshipLevel++;

        if ((event != FRIENDSHIP_EVENT_WALKING || !(Random() & 1))
            && (event != FRIENDSHIP_EVENT_LEAGUE_BATTLE || IS_LEAGUE_BATTLE))
        {
            s8 mod = sFriendshipEventModifiers[event][friendshipLevel];
            if (mod > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
                mod = (150 * mod) / 100;
            friendship += mod;
            if (mod > 0)
            {
                if (GetMonData(mon, MON_DATA_POKEBALL, 0) == ITEM_LUXURY_BALL)
                    friendship++;
                if (GetMonData(mon, MON_DATA_MET_LOCATION, 0) == GetCurrentRegionMapSectionId())
                    friendship++;
            }
            if (friendship < 0)
                friendship = 0;
            if (friendship > MAX_FRIENDSHIP)
                friendship = MAX_FRIENDSHIP;
            SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
        }
    }
}

void MonGainEVs(struct Pokemon *mon, u16 defeatedSpecies)
{
    u8 evs[NUM_STATS];
    u16 evIncrease = 0;
    u16 totalEVs = 0;
    u16 heldItem;
    u8 holdEffect;
    int i, multiplier;

    for (i = 0; i < NUM_STATS; i++)
    {
        evs[i] = GetMonData(mon, MON_DATA_HP_EV + i, 0);
        totalEVs += evs[i];
    }

    for (i = 0; i < NUM_STATS; i++)
    {
        if (totalEVs >= MAX_TOTAL_EVS)
            break;

        if (CheckPartyHasHadPokerus(mon, 0))
            multiplier = 2;
        else
            multiplier = 1;

        switch (i)
        {
            case STAT_HP:
                evIncrease = gBaseStats[defeatedSpecies].evYield_HP * multiplier;
                break;
            case STAT_ATK:
                evIncrease = gBaseStats[defeatedSpecies].evYield_Attack * multiplier;
                break;
            case STAT_DEF:
                evIncrease = gBaseStats[defeatedSpecies].evYield_Defense * multiplier;
                break;
            case STAT_SPEED:
                evIncrease = gBaseStats[defeatedSpecies].evYield_Speed * multiplier;
                break;
            case STAT_SPATK:
                evIncrease = gBaseStats[defeatedSpecies].evYield_SpAttack * multiplier;
                break;
            case STAT_SPDEF:
                evIncrease = gBaseStats[defeatedSpecies].evYield_SpDefense * multiplier;
                break;
        }

        heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0);
        if (heldItem == ITEM_ENIGMA_BERRY)
        {
            if (gMain.inBattle)
                holdEffect = gEnigmaBerries[0].holdEffect;
            else
                holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
        }
        else
        {
            holdEffect = ItemId_GetHoldEffect(heldItem);
        }
        if (holdEffect == HOLD_EFFECT_EV_BOOST && ItemId_GetSecondaryId(heldItem) == i)
            evIncrease += 8;
        else if (holdEffect == HOLD_EFFECT_MACHO_BRACE)
            evIncrease *= 2;

        if (totalEVs + (s16)evIncrease > MAX_TOTAL_EVS)
            evIncrease = ((s16)evIncrease + MAX_TOTAL_EVS) - (totalEVs + evIncrease);

        if (evs[i] + (s16)evIncrease > MAX_PER_STAT_EVS)
        {
            int val1 = (s16)evIncrease + MAX_PER_STAT_EVS;
            int val2 = evs[i] + evIncrease;
            evIncrease = val1 - val2;
        }

        evs[i] += evIncrease;
        totalEVs += evIncrease;
        SetMonData(mon, MON_DATA_HP_EV + i, &evs[i]);
    }
}

u16 GetMonEVCount(struct Pokemon *mon)
{
    int i;
    u16 count = 0;

    for (i = 0; i < NUM_STATS; i++)
        count += GetMonData(mon, MON_DATA_HP_EV + i, 0);

    return count;
}

void RandomlyGivePartyPokerus(struct Pokemon *party)
{
    u16 rnd = Random();
    if (rnd == 0x4000 || rnd == 0x8000 || rnd == 0xC000)
    {
        struct Pokemon *mon;

        do
        {
            do
            {
                rnd = Random() % PARTY_SIZE;
                mon = &party[rnd];
            }
            while (!GetMonData(mon, MON_DATA_SPECIES, 0));
        }
        while (GetMonData(mon, MON_DATA_IS_EGG, 0));

        if (!(CheckPartyHasHadPokerus(party, gBitTable[rnd])))
        {
            u8 rnd2;

            do
            {
                rnd2 = Random();
            }
            while ((rnd2 & 0x7) == 0);

            if (rnd2 & 0xF0)
                rnd2 &= 0x7;

            rnd2 |= (rnd2 << 4);
            rnd2 &= 0xF3;
            rnd2++;

            SetMonData(&party[rnd], MON_DATA_POKERUS, &rnd2);
        }
    }
}

u8 CheckPartyPokerus(struct Pokemon *party, u8 selection)
{
    u8 retVal;

    int partyIndex = 0;
    unsigned curBit = 1;
    retVal = 0;

    if (selection)
    {
        do
        {
            if ((selection & 1) && (GetMonData(&party[partyIndex], MON_DATA_POKERUS, 0) & 0xF))
                retVal |= curBit;
            partyIndex++;
            curBit <<= 1;
            selection >>= 1;
        }
        while (selection);
    }
    else if (GetMonData(&party[0], MON_DATA_POKERUS, 0) & 0xF)
    {
        retVal = 1;
    }

    return retVal;
}

u8 CheckPartyHasHadPokerus(struct Pokemon *party, u8 selection)
{
    u8 retVal;

    int partyIndex = 0;
    unsigned curBit = 1;
    retVal = 0;

    if (selection)
    {
        do
        {
            if ((selection & 1) && GetMonData(&party[partyIndex], MON_DATA_POKERUS, 0))
                retVal |= curBit;
            partyIndex++;
            curBit <<= 1;
            selection >>= 1;
        }
        while (selection);
    }
    else if (GetMonData(&party[0], MON_DATA_POKERUS, 0))
    {
        retVal = 1;
    }

    return retVal;
}

void UpdatePartyPokerusTime(u16 days)
{
    int i;
    for (i = 0; i < PARTY_SIZE; i++)
    {
        if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, 0))
        {
            u8 pokerus = GetMonData(&gPlayerParty[i], MON_DATA_POKERUS, 0);
            if (pokerus & 0xF)
            {
                if ((pokerus & 0xF) < days || days > 4)
                    pokerus &= 0xF0;
                else
                    pokerus -= days;

                if (pokerus == 0)
                    pokerus = 0x10;

                SetMonData(&gPlayerParty[i], MON_DATA_POKERUS, &pokerus);
            }
        }
    }
}

void PartySpreadPokerus(struct Pokemon *party)
{
    if ((Random() % 3) == 0)
    {
        int i;
        for (i = 0; i < PARTY_SIZE; i++)
        {
            if (GetMonData(&party[i], MON_DATA_SPECIES, 0))
            {
                u8 pokerus = GetMonData(&party[i], MON_DATA_POKERUS, 0);
                u8 curPokerus = pokerus;
                if (pokerus)
                {
                    if (pokerus & 0xF)
                    {
                        // Spread to adjacent party members.
                        if (i != 0 && !(GetMonData(&party[i - 1], MON_DATA_POKERUS, 0) & 0xF0))
                            SetMonData(&party[i - 1], MON_DATA_POKERUS, &curPokerus);
                        if (i != (PARTY_SIZE - 1) && !(GetMonData(&party[i + 1], MON_DATA_POKERUS, 0) & 0xF0))
                        {
                            SetMonData(&party[i + 1], MON_DATA_POKERUS, &curPokerus);
                            i++;
                        }
                    }
                }
            }
        }
    }
}

bool8 TryIncrementMonLevel(struct Pokemon *mon)
{
    u16 species = GetMonData(mon, MON_DATA_SPECIES, 0);
    u8 nextLevel = GetMonData(mon, MON_DATA_LEVEL, 0) + 1;
    u32 expPoints = GetMonData(mon, MON_DATA_EXP, 0);
    if (expPoints > gExperienceTables[gBaseStats[species].growthRate][VarGet(VAR_MAX_LEVEL)])
    {
        expPoints = gExperienceTables[gBaseStats[species].growthRate][VarGet(VAR_MAX_LEVEL)];
        SetMonData(mon, MON_DATA_EXP, &expPoints);
    }
    if (nextLevel > VarGet(VAR_MAX_LEVEL) || expPoints < gExperienceTables[gBaseStats[species].growthRate][nextLevel])
    {
        return FALSE;
    }
    else
    {
        SetMonData(mon, MON_DATA_LEVEL, &nextLevel);
        return TRUE;
    }
}

u16 GetEvoBaseSpecies(u16 species) {
    u8 i = 0;
    u16 j = 0;
    for (; j < NUM_SPECIES; j++)
        for (; i < EVOS_PER_MON; i++) {
            if (gEvolutionTable[j][i].targetSpecies == species)
                return j;
        }
    return species;
}

u32 CanMonLearnTMHM(struct Pokemon *mon, u8 tm)
{
    u16 species = GetMonData(mon, MON_DATA_SPECIES3, 0);
    return CanSpeciesLearnTMHM(species, tm);
}

u32 CanSpeciesLearnTMHM(u16 species, u8 tm)
{
    if (species == 0)
    {
        return 0;
    }
    else if (tm < 100)
    {
        u32 mask = 1 << tm % 8;
        return gTMLearnsets[SpeciesToNationalPokedexNum(species)].data[tm / 8] & mask;
    }
    else if (tm < 108)
    {
        tm -= 100;
        return (gHMLearnsets[species].data & ( 1 << tm));
    }
    else {
        return 0;
    }
}

u8 GetMoveRelearnerMoves(struct Pokemon *mon, u16 *moves)
{
    u16 learnedMoves[MAX_MON_MOVES];
    u8 numMoves = 0;
    u16 species = GetMonData(mon, MON_DATA_SPECIES, 0);
    u8 level = GetMonData(mon, MON_DATA_LEVEL, 0);
    int i, j, k;

    for (i = 0; i < MAX_MON_MOVES; i++)
        learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, 0);

    for (i = 0; i < MAX_LEVEL_UP_MOVES; i++)
    {
        u16 moveLevel;

        if (gLevelUpLearnsets[species][i].move == LEVEL_UP_END)
            break;

        moveLevel = gLevelUpLearnsets[species][i].level;

        if (moveLevel <= level)
        {
            for (j = 0; j < MAX_MON_MOVES && learnedMoves[j] != gLevelUpLearnsets[species][i].move; j++)
                ;

            if (j == MAX_MON_MOVES)
            {
                for (k = 0; k < numMoves && moves[k] != gLevelUpLearnsets[species][i].move; k++)
                    ;

                if (k == numMoves)
                    moves[numMoves++] = gLevelUpLearnsets[species][i].move;
            }
        }
    }

    return numMoves;
}
//这里只有孵蛋的时候会使用，所以不必修改。
u8 GetLevelUpMovesBySpecies(u16 species, u16 *moves)
{
    u8 numMoves = 0;
    int i;

    for (i = 0; i < MAX_LEVEL_UP_MOVES && gLevelUpLearnsets[species][i].move != LEVEL_UP_END; i++)
        moves[numMoves++] = gLevelUpLearnsets[species][i].move;

    return numMoves;
}

u8 GetNumberOfRelearnableMoves(struct Pokemon *mon)
{
    u16 learnedMoves[MAX_MON_MOVES];
    u16 moves[MAX_LEVEL_UP_MOVES];
    u8 numMoves = 0;
    u16 species = GetMonData(mon, MON_DATA_SPECIES2, 0);
    u8 level = GetMonData(mon, MON_DATA_LEVEL, 0);
    int i, j, k;

    if (species == SPECIES_EGG)
        return 0;

    for (i = 0; i < MAX_MON_MOVES; i++)
        learnedMoves[i] = GetMonData(mon, MON_DATA_MOVE1 + i, 0);

    for (i = 0; i < MAX_LEVEL_UP_MOVES; i++)
    {
        u16 moveLevel;

        if (gLevelUpLearnsets[species][i].move == LEVEL_UP_END)
            break;

        moveLevel = gLevelUpLearnsets[species][i].level;

        if (moveLevel <= level)
        {
            for (j = 0; j < MAX_MON_MOVES && learnedMoves[j] != gLevelUpLearnsets[species][i].move; j++)
                ;

            if (j == MAX_MON_MOVES)
            {
                for (k = 0; k < numMoves && moves[k] != gLevelUpLearnsets[species][i].move; k++)
                    ;

                if (k == numMoves)
                    moves[numMoves++] = gLevelUpLearnsets[species][i].move;
            }
        }
    }

    return numMoves + GetEggMoves(mon, moves);
}

u16 SpeciesToPokedexNum(u16 species)
{
    if (IsNationalPokedexEnabled())
    {
        return SpeciesToNationalPokedexNum(species);
    }
    else
    {
        species = SpeciesToHoennPokedexNum(species);
        if (species <= HOENN_DEX_COUNT)
            return species;
        return 0xFFFF;
    }
}

bool32 IsSpeciesInHoennDex(u16 species)
{
    if (SpeciesToHoennPokedexNum(species) > HOENN_DEX_COUNT)
        return FALSE;
    else
        return TRUE;
}

void ClearBattleMonForms(void)
{
    int i;
    for (i = 0; i < MAX_BATTLERS_COUNT; i++)
        gBattleMonForms[i] = 0;
}

u16 GetBattleBGM(void)
{
    if (gBattleTypeFlags & BATTLE_TYPE_KYOGRE_GROUDON)
        return MUS_VS_KYOGRE_GROUDON;
    else if (gBattleTypeFlags & BATTLE_TYPE_REGI)
        return MUS_VS_REGI;
    else if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000))
        return MUS_VS_TRAINER;
    else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
    {
        u8 trainerClass;

        if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER)
            trainerClass = GetFrontierOpponentClass(gTrainerBattleOpponent_A);
        else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL)
            trainerClass = TRAINER_CLASS_EXPERT;
        else
            trainerClass = gTrainers[gTrainerBattleOpponent_A].trainerClass;

        switch (trainerClass)
        {
            case TRAINER_CLASS_AQUA_LEADER:
            case TRAINER_CLASS_MAGMA_LEADER:
                return MUS_VS_AQUA_MAGMA_LEADER;
            case TRAINER_CLASS_TEAM_AQUA:
            case TRAINER_CLASS_TEAM_MAGMA:
            case TRAINER_CLASS_AQUA_ADMIN:
            case TRAINER_CLASS_MAGMA_ADMIN:
                return MUS_VS_AQUA_MAGMA;
            case TRAINER_CLASS_LEADER:
                return MUS_VS_GYM_LEADER;
            case TRAINER_CLASS_CHAMPION:
                return MUS_VS_CHAMPION;
            case TRAINER_CLASS_PKMN_TRAINER_3:
                if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER)
                    return MUS_VS_RIVAL;
                if (!StringCompare(gTrainers[gTrainerBattleOpponent_A].trainerName, gText_BattleWallyName))
                    return MUS_VS_TRAINER;
                return MUS_VS_RIVAL;
            case TRAINER_CLASS_ELITE_FOUR:
                return MUS_VS_ELITE_FOUR;
            case TRAINER_CLASS_SALON_MAIDEN:
            case TRAINER_CLASS_DOME_ACE:
            case TRAINER_CLASS_PALACE_MAVEN:
            case TRAINER_CLASS_ARENA_TYCOON:
            case TRAINER_CLASS_FACTORY_HEAD:
            case TRAINER_CLASS_PIKE_QUEEN:
            case TRAINER_CLASS_PYRAMID_KING:
                return MUS_VS_FRONTIER_BRAIN;
            default:
                return MUS_VS_TRAINER;
        }
    }
    else
        return MUS_VS_WILD;
}

void PlayBattleBGM(void)
{
    ResetMapMusic();
    m4aMPlayAllStop();
    PlayBGM(GetBattleBGM());
}

void PlayMapChosenOrBattleBGM(u16 songId)
{
    ResetMapMusic();
    m4aMPlayAllStop();
    if (songId)
        PlayNewMapMusic(songId);
    else
        PlayNewMapMusic(GetBattleBGM());
}

void sub_806E694(u16 songId)
{
    u8 taskId;

    ResetMapMusic();
    m4aMPlayAllStop();

    taskId = CreateTask(sub_806E6CC, 0);
    gTasks[taskId].data[0] = songId;
}

static void sub_806E6CC(u8 taskId)
{
    if (gTasks[taskId].data[0])
        PlayNewMapMusic(gTasks[taskId].data[0]);
    else
        PlayNewMapMusic(GetBattleBGM());
    DestroyTask(taskId);
}

const u32 *GetMonFrontSpritePal(struct Pokemon *mon)
{
    u16 species = GetMonData(mon, MON_DATA_SPECIES2, 0);
    u32 otId = GetMonData(mon, MON_DATA_OT_ID, 0);
    u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0);
    return GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality);
}

const u32 *GetMonSpritePalFromSpeciesAndPersonality(u16 species, u32 otId, u32 personality)
{
    u32 shinyValue;

    if (species > NUM_SPECIES_PIC)
        return gMonPaletteTable[0].data;

    shinyValue = HIHALF(otId) ^ LOHALF(otId) ^ HIHALF(personality) ^ LOHALF(personality);
    if (shinyValue < SHINY_ODDS)
        return gMonShinyPaletteTable[species].data;
    else
        return gMonPaletteTable[species].data;
}

const struct CompressedSpritePalette *GetMonSpritePalStruct(struct Pokemon *mon)
{
    u16 species = GetMonData(mon, MON_DATA_SPECIES2, 0);
    u32 otId = GetMonData(mon, MON_DATA_OT_ID, 0);
    u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0);
    return GetMonSpritePalStructFromOtIdPersonality(species, otId, personality);
}

const struct CompressedSpritePalette *GetMonSpritePalStructFromOtIdPersonality(u16 species, u32 otId , u32 personality)
{
    u32 shinyValue;

    shinyValue = HIHALF(otId) ^ LOHALF(otId) ^ HIHALF(personality) ^ LOHALF(personality);
    if (shinyValue < SHINY_ODDS)
        return &gMonShinyPaletteTable[species];
    else
        return &gMonPaletteTable[species];
}

bool32 IsHMMove2(u16 move)
{
    int i = 0;
    while (sHMMoves[i] != 0xFFFF)
    {
        if (sHMMoves[i++] == move)
            return TRUE;
    }
    return FALSE;
}

bool8 IsMonSpriteNotFlipped(u16 species)
{
    return gBaseStats[species].noFlip;
}

s8 GetMonFlavorRelation(struct Pokemon *mon, u8 flavor)
{
    u8 nature = GetNature(mon);
    return gPokeblockFlavorCompatibilityTable[nature * FLAVOR_COUNT + flavor];
}

s8 GetFlavorRelationByPersonality(u32 personality, u8 flavor)
{
    u8 nature = GetNatureFromPersonality(personality);
    return gPokeblockFlavorCompatibilityTable[nature * FLAVOR_COUNT + flavor];
}

bool8 IsTradedMon(struct Pokemon *mon)
{
    /*u8 otName[PLAYER_NAME_LENGTH + 1];
    u32 otId;
    GetMonData(mon, MON_DATA_OT_NAME, otName);
    otId = GetMonData(mon, MON_DATA_OT_ID, 0);
    return IsOtherTrainer(otId, otName);*/
    return mon->box.isOtherTrainer;
}

bool8 IsOtherTrainer(u32 otId, u8 *otName)
{
    /*if (otId ==
        (gSaveBlock2Ptr->playerTrainerId[0]
         | (gSaveBlock2Ptr->playerTrainerId[1] << 8)
         | (gSaveBlock2Ptr->playerTrainerId[2] << 16)
         | (gSaveBlock2Ptr->playerTrainerId[3] << 24)))
    {
        int i;

        for (i = 0; otName[i] != EOS; i++)
            if (otName[i] != gSaveBlock2Ptr->playerName[i])
                return TRUE;
        return FALSE;
    }

    return TRUE;*/return FALSE;
}

void MonRestorePP(struct Pokemon *mon)
{
    int i;

    for (i = 0; i < MAX_MON_MOVES; i++)
    {
        if (GetMonData(mon, MON_DATA_MOVE1 + i, 0))
        {
            u16 move = GetMonData(mon, MON_DATA_MOVE1 + i, 0);
            u16 bonus = GetMonData(mon, MON_DATA_PP_BONUSES, 0);
            u8 pp = CalculatePPWithBonus(move, bonus, i);
            SetMonData(mon, MON_DATA_PP1 + i, &pp);
        }
    }
}

//void BoxMonRestorePP(struct BoxPokemon *boxMon) //no use
//{
//    int i;
//
//    for (i = 0; i < MAX_MON_MOVES; i++)
//    {
//        if (GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, 0))
//        {
//            u16 move = GetBoxMonData(boxMon, MON_DATA_MOVE1 + i, 0);
//            u16 bonus = GetBoxMonData(boxMon, MON_DATA_PP_BONUSES, 0);
//            u8 pp = CalculatePPWithBonus(move, bonus, i);
//            SetBoxMonData(boxMon, MON_DATA_PP1 + i, &pp);
//        }
//    }
//}

void SetMonPreventsSwitchingString(void)
{
    gLastUsedAbility = gBattleStruct->abilityPreventingSwitchout;

    gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN;
    gBattleTextBuff1[1] = B_BUFF_MON_NICK_WITH_PREFIX;
    gBattleTextBuff1[2] = gBattleStruct->battlerPreventingSwitchout;
    gBattleTextBuff1[4] = B_BUFF_EOS;

    if (GetBattlerSide(gBattleStruct->battlerPreventingSwitchout) == B_SIDE_PLAYER)
        gBattleTextBuff1[3] = GetPartyIdFromBattlePartyId(gBattlerPartyIndexes[gBattleStruct->battlerPreventingSwitchout]);
    else
        gBattleTextBuff1[3] = gBattlerPartyIndexes[gBattleStruct->battlerPreventingSwitchout];

    PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff2, gBattlerInMenuId, GetPartyIdFromBattlePartyId(gBattlerPartyIndexes[gBattlerInMenuId]))

    BattleStringExpandPlaceholders(gText_PkmnsXPreventsSwitching, gStringVar4);
}

static s32 GetWildMonTableIdInAlteringCave(u16 species)
{
    s32 i;
    for (i = 0; i < (s32) ARRAY_COUNT(sAlteringCaveWildMonHeldItems); i++)
        if (sAlteringCaveWildMonHeldItems[i].species == species)
            return i;
    return 0;
}

void SetWildMonHeldItem(void)
{
    u16 rnd, species, var1, var2, i, count;
    if (gBattleTypeFlags & (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_TRAINER | BATTLE_TYPE_PYRAMID | BATTLE_TYPE_PIKE))
        return;
    var1 = GetMonAbility(&gPlayerParty[0]);
    count = (WILD_DOUBLE_BATTLE) ? 2 : 1;
    if (!GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG, 0)
        && (var1 == ABILITY_COMPOUND_EYES
            || var1 == ABILITY_SUPER_LUCK || var1 == ABILITY_SYNCHRONIZE))
    {
        var1 = 20;
        var2 = 80;
    }
    else
    {
        var1 = 45;
        var2 = 95;
    }

    for (i = 0; i < count; i++)
    {
    	if (GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, NULL) != 0)
    		continue;
        rnd = Random() % 100;
        species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES, 0);
        if (gMapHeader.mapLayoutId == LAYOUT_ALTERING_CAVE)
        {
            s32 alteringCaveId = GetWildMonTableIdInAlteringCave(species);
            if (alteringCaveId != 0)
            {
                if (rnd < var2)
                    continue;
                SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM,
                           &sAlteringCaveWildMonHeldItems[alteringCaveId].item);
            }
            else
            {
                if (rnd < var1)
                    continue;
                if (rnd < var2)
                    SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gBaseStats[species].item1);
                else
                    SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gBaseStats[species].item2);
            }
        }
        else
        {
            if (gBaseStats[species].item1 == gBaseStats[species].item2 && gBaseStats[species].item1 != 0)
            {
                SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gBaseStats[species].item1);
            }
            else
            {
                if (rnd < var1)
                    continue;
                if (rnd < var2)
                    SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gBaseStats[species].item1);
                else
                    SetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM, &gBaseStats[species].item2);
            }
        }
    }
}

bool8 IsMonShiny(struct Pokemon *mon)
{
    u32 otId = GetMonData(mon, MON_DATA_OT_ID, 0);
    u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0);
    return IsShinyOtIdPersonality(otId, personality);
}

bool8 IsShinyOtIdPersonality(u32 otId, u32 personality)
{
    bool8 retVal = FALSE;
    u32 shinyValue = HIHALF(otId) ^ LOHALF(otId) ^ HIHALF(personality) ^ LOHALF(personality);
    if (shinyValue < SHINY_ODDS)
        retVal = TRUE;
    return retVal;
}

const u8 *GetTrainerPartnerName(void)
{
    if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
    {
        if (gPartnerTrainerId == TRAINER_STEVEN_PARTNER)
        {
            return gTrainers[TRAINER_STEVEN].trainerName;
        }
        else
        {
            GetFrontierTrainerName(gStringVar1, gPartnerTrainerId);
            return gStringVar1;
        }
    }
    else
    {
        u8 id = GetMultiplayerId();
        return gLinkPlayers[GetBattlerMultiplayerId(gLinkPlayers[id].id ^ 2)].name;
    }
}

#define READ_PTR_FROM_TASK(taskId, dataId)                      \
    (void*)(                                                    \
    ((u16)(gTasks[taskId].data[dataId]) |                       \
    ((u16)(gTasks[taskId].data[dataId + 1]) << 0x10)))

#define STORE_PTR_IN_TASK(ptr, taskId, dataId)                 \
{                                                              \
    gTasks[taskId].data[dataId] = (u32)(ptr);                  \
    gTasks[taskId].data[dataId + 1] = (u32)(ptr) >> 0x10;      \
}

static void Task_AnimateAfterDelay(u8 taskId)
{
    if (--gTasks[taskId].data[3] == 0)
    {
        LaunchAnimationTaskForFrontSprite(READ_PTR_FROM_TASK(taskId, 0), gTasks[taskId].data[2]);
        DestroyTask(taskId);
    }
}

static void Task_PokemonSummaryAnimateAfterDelay(u8 taskId)
{
    if (--gTasks[taskId].data[3] == 0)
    {
        StartMonSummaryAnimation(READ_PTR_FROM_TASK(taskId, 0), gTasks[taskId].data[2]);
        SummaryScreen_SetUnknownTaskId(0xFF);
        DestroyTask(taskId);
    }
}

void BattleAnimateFrontSprite(struct Sprite* sprite, u16 species, bool8 noCry, u8 arg3)
{
    if (gHitMarker & HITMARKER_NO_ANIMATIONS && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000)))
        DoMonFrontSpriteAnimation(sprite, species, noCry, arg3 | 0x80);
    else
        DoMonFrontSpriteAnimation(sprite, species, noCry, arg3);
}

void DoMonFrontSpriteAnimation(struct Sprite* sprite, u16 species, bool8 noCry, u8 arg3)
{
    s8 pan;
    switch (arg3 & 0x7F)
    {
        case 0:
            pan = -25;
            break;
        case 1:
            pan = 25;
            break;
        default:
            pan = 0;
            break;
    }
    if (arg3 & 0x80)
    {
        if (!noCry)
            PlayCry1(species, pan);
        sprite->callback = SpriteCallbackDummy;
    }
    else
    {
        if (!noCry)
        {
            PlayCry1(species, pan);
            if (HasTwoFramesAnimation(species))
                StartSpriteAnim(sprite, 1);
        }
        if (sMonAnimationDelayTable[species - 1] != 0)
        {
            u8 taskId = CreateTask(Task_AnimateAfterDelay, 0);
            STORE_PTR_IN_TASK(sprite, taskId, 0);
            gTasks[taskId].data[2] = sMonFrontAnimIdsTable[species - 1];
            gTasks[taskId].data[3] = sMonAnimationDelayTable[species - 1];
        }
        else
        {
            LaunchAnimationTaskForFrontSprite(sprite, sMonFrontAnimIdsTable[species - 1]);
        }
        sprite->callback = SpriteCallbackDummy_2;
    }
}

void PokemonSummaryDoMonAnimation(struct Sprite* sprite, u16 species, bool8 oneFrame)
{
    if (!oneFrame && HasTwoFramesAnimation(species))
        StartSpriteAnim(sprite, 1);
    if (sMonAnimationDelayTable[species - 1] != 0)
    {
        u8 taskId = CreateTask(Task_PokemonSummaryAnimateAfterDelay, 0);
        STORE_PTR_IN_TASK(sprite, taskId, 0);
        gTasks[taskId].data[2] = sMonFrontAnimIdsTable[species - 1];
        gTasks[taskId].data[3] = sMonAnimationDelayTable[species - 1];
        SummaryScreen_SetUnknownTaskId(taskId);
        SetSpriteCB_MonAnimDummy(sprite);
    }
    else
    {
        StartMonSummaryAnimation(sprite, sMonFrontAnimIdsTable[species - 1]);
    }
}

void StopPokemonAnimationDelayTask(void)
{
    u8 delayTaskId = FindTaskIdByFunc(Task_PokemonSummaryAnimateAfterDelay);
    if (delayTaskId != 0xFF)
        DestroyTask(delayTaskId);
}

void BattleAnimateBackSprite(struct Sprite* sprite, u16 species)
{
    if (gHitMarker & HITMARKER_NO_ANIMATIONS && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000)))
    {
        sprite->callback = SpriteCallbackDummy;
    }
    else
    {
        LaunchAnimationTaskForBackSprite(sprite, GetSpeciesBackAnimSet(species));
        sprite->callback = SpriteCallbackDummy_2;
    }
}

/*u8 sub_806EF08(u8 arg0)
{
    s32 i;
    s32 var = 0;
    u8 multiplayerId = GetMultiplayerId();
    switch (gLinkPlayers[multiplayerId].id)
    {
        case 0:
        case 2:
            var = (arg0 != 0) ? 1 : 3;
            break;
        case 1:
        case 3:
            var = (arg0 != 0) ? 2 : 0;
            break;
    }
    for (i = 0; i < MAX_LINK_PLAYERS; i++)
    {
        if (gLinkPlayers[i].id == (s16)(var))
            break;
    }
    return i;
}*/

u8 sub_806EF84(u8 arg0, u8 arg1)
{
    s32 i;
    s32 var = 0;
    switch (gLinkPlayers[arg1].id)
    {
        case 0:
        case 2:
            var = (arg0 != 0) ? 1 : 3;
            break;
        case 1:
        case 3:
            var = (arg0 != 0) ? 2 : 0;
            break;
    }
    for (i = 0; i < MAX_LINK_PLAYERS; i++)
    {
        if (gLinkPlayers[i].id == (s16)(var))
            break;
    }
    return i;
}

u16 FacilityClassToPicIndex(u16 facilityClass)
{
    return gFacilityClassToPicIndex[facilityClass];
}

u16 PlayerGenderToFrontTrainerPicId(u8 playerGender)
{
    if (playerGender != MALE)
        return FacilityClassToPicIndex(FACILITY_CLASS_MAY);
    else
        return FacilityClassToPicIndex(FACILITY_CLASS_BRENDAN);
}

void HandleSetPokedexFlag(u16 nationalNum, u8 caseId, u32 personality)
{
    u8 getFlagCaseId = (caseId == FLAG_SET_SEEN) ? FLAG_GET_SEEN : FLAG_GET_CAUGHT;
    if (!GetSetPokedexFlag(nationalNum, getFlagCaseId)) // don't set if it's already set
    {
        GetSetPokedexFlag(nationalNum, caseId);
        if (NationalPokedexNumToSpecies(nationalNum) == SPECIES_UNOWN)
            gSaveBlock2Ptr->pokedex.unownPersonality = personality;
        if (NationalPokedexNumToSpecies(nationalNum) == SPECIES_SPINDA)
            gSaveBlock2Ptr->pokedex.spindaPersonality = personality;
    }
    if (caseId == FLAG_SET_SEEN)
        TryIncrementSpeciesSearchLevel(nationalNum);    //encountering pokemon increments its search level
}

const u8 *GetTrainerClassNameFromId(u16 trainerId)
{
    if (trainerId >= TRAINERS_COUNT)
        trainerId = TRAINER_NONE;
    return gTrainerClassNames[gTrainers[trainerId].trainerClass];
}

const u8 *GetTrainerNameFromId(u16 trainerId)
{
    if (trainerId >= TRAINERS_COUNT)
        trainerId = TRAINER_NONE;
    return gTrainers[trainerId].trainerName;
}

bool8 HasTwoFramesAnimation(u16 species)
{
    return (species != SPECIES_CASTFORM
            && species != SPECIES_DEOXYS
            && species != SPECIES_SPINDA
            && species != SPECIES_UNOWN
            && species != SPECIES_CHERRIM);
}

static bool8 ShouldSkipFriendshipChange(void)
{
    if (gMain.inBattle && gBattleTypeFlags & (BATTLE_TYPE_FRONTIER))
        return TRUE;
    if (!gMain.inBattle && (InBattlePike() || InBattlePyramid()))
        return TRUE;
    return FALSE;
}

#define FORCE_SIGNED(x)(-(x * (-1)))

static void sub_806F160(struct Unknown_806F160_Struct* structPtr)
{
    u16 i, j;
    for (i = 0; i < FORCE_SIGNED(structPtr->field_0_0); i++)
    {
        structPtr->templates[i] = gUnknown_08329D98[i];
        for (j = 0; j < structPtr->field_1; j++)
        {
#ifndef NONMATCHING
            asm("");
#endif
            structPtr->frameImages[i * structPtr->field_1 + j].data = &structPtr->byteArrays[i][j * 0x800];
        }
        structPtr->templates[i].images = &structPtr->frameImages[i * structPtr->field_1];
    }
}

static void sub_806F1FC(struct Unknown_806F160_Struct* structPtr)
{
    u16 i, j;
    for (i = 0; i < FORCE_SIGNED(structPtr->field_0_0); i++)
    {
        structPtr->templates[i] = gUnknown_08329F28;
        for (j = 0; j < structPtr->field_1; j++)
        {
            structPtr->frameImages[i * structPtr->field_0_0 + j].data = &structPtr->byteArrays[i][j * 0x800];
        }
        structPtr->templates[i].images = &structPtr->frameImages[i * structPtr->field_0_0];
        structPtr->templates[i].anims = gUnknown_082FF70C;
        structPtr->templates[i].paletteTag = i;
    }
}

struct Unknown_806F160_Struct *sub_806F2AC(u8 id, u8 arg1)
{
    u8 i;
    u8 flags;
    struct Unknown_806F160_Struct *structPtr;

    flags = 0;
    id %= 2;
    structPtr = AllocZeroed(sizeof(*structPtr));
    if (structPtr == NULL)
        return NULL;

    switch (arg1)
    {
        case 2:
            structPtr->field_0_0 = 7;
            structPtr->field_0_1 = 7;
            structPtr->field_1 = 4;
            structPtr->field_3_0 = 1;
            structPtr->field_3_1 = 2;
            break;
        case 0:
        default:
            structPtr->field_0_0 = 4;
            structPtr->field_0_1 = 4;
            structPtr->field_1 = 4;
            structPtr->field_3_0 = 1;
            structPtr->field_3_1 = 0;
            break;
    }

    structPtr->bytes = AllocZeroed(structPtr->field_3_0 * 0x800 * 4 * structPtr->field_0_0);
    structPtr->byteArrays = AllocZeroed(structPtr->field_0_0 * 32);
    if (structPtr->bytes == NULL || structPtr->byteArrays == NULL)
    {
        flags |= 1;
    }
    else
    {
        for (i = 0; i < FORCE_SIGNED(structPtr->field_0_0); i++)
            structPtr->byteArrays[i] = structPtr->bytes + (structPtr->field_3_0 * (i << 0xD));
    }

    structPtr->templates = AllocZeroed(sizeof(struct SpriteTemplate) * structPtr->field_0_0);
    structPtr->frameImages = AllocZeroed(sizeof(struct SpriteFrameImage) * structPtr->field_0_0 * structPtr->field_1);
    if (structPtr->templates == NULL || structPtr->frameImages == NULL)
    {
        flags |= 2;
    }
    else
    {
        for (i = 0; i < structPtr->field_1 * structPtr->field_0_0; i++)
            structPtr->frameImages[i].size = 0x800;

        switch (structPtr->field_3_1)
        {
        case 2:
            sub_806F1FC(structPtr);
            break;
        case 0:
        case 1:
        default:
            sub_806F160(structPtr);
            break;
        }
    }

    if (flags & 2)
    {
        if (structPtr->frameImages != NULL)
        FREE_AND_SET_NULL(structPtr->frameImages);
        if (structPtr->templates != NULL)
        FREE_AND_SET_NULL(structPtr->templates);
    }
    if (flags & 1)
    {
        if (structPtr->byteArrays != NULL)
        FREE_AND_SET_NULL(structPtr->byteArrays);
        if (structPtr->bytes != NULL)
        FREE_AND_SET_NULL(structPtr->bytes);
    }

    if (flags)
    {
        memset(structPtr, 0, sizeof(*structPtr));
        Free(structPtr);
    }
    else
    {
        structPtr->magic = 0xA3;
        gUnknown_020249B4[id] = structPtr;
    }

    return gUnknown_020249B4[id];
}

void sub_806F47C(u8 id)
{
    struct Unknown_806F160_Struct *structPtr;

    id %= 2;
    structPtr = gUnknown_020249B4[id];
    if (structPtr == NULL)
        return;

    if (structPtr->magic != 0xA3)
    {
        memset(structPtr, 0, sizeof(struct Unknown_806F160_Struct));
    }
    else
    {

        if (structPtr->frameImages != NULL)
        FREE_AND_SET_NULL(structPtr->frameImages);
        if (structPtr->templates != NULL)
        FREE_AND_SET_NULL(structPtr->templates);
        if (structPtr->byteArrays != NULL)
            FREE_AND_SET_NULL(structPtr->byteArrays);
        if (structPtr->bytes != NULL)
        FREE_AND_SET_NULL(structPtr->bytes);

        memset(structPtr, 0, sizeof(struct Unknown_806F160_Struct));
        Free(structPtr);
    }
}

u8 *sub_806F4F8(u8 id, u8 arg1)
{
    struct Unknown_806F160_Struct *structPtr = gUnknown_020249B4[id % 2];
    if (structPtr->magic != 0xA3)
    {
        return NULL;
    }
    else
    {
        if (arg1 >= FORCE_SIGNED(structPtr->field_0_0))
            arg1 = 0;

        return structPtr->byteArrays[arg1];
    }
}

#include "data/pokemon/form_species_tables.h"
#include "data/pokemon/form_species_table_pointers.h"
#include "data/pokemon/form_change_tables.h"
#include "data/pokemon/form_change_table_pointers.h"

u16 GetFormSpeciesId(u16 speciesId, u8 formId)
{
    if (gFormSpeciesIdTables[speciesId] != NULL)
        return gFormSpeciesIdTables[speciesId][formId];
    else
        return speciesId;
}

u8 GetFormIdFromFormSpeciesId(u16 formSpeciesId)
{
    u8 targetFormId = 0;

    if (gFormSpeciesIdTables[formSpeciesId] != NULL)
    {
        for (targetFormId = 0; gFormSpeciesIdTables[formSpeciesId][targetFormId] != FORM_SPECIES_END; targetFormId++)
        {
            if (formSpeciesId == gFormSpeciesIdTables[formSpeciesId][targetFormId])
                break;
        }
    }
    return targetFormId;
}

bool8 IsFormChangeFusedSpecies(struct Pokemon *mon, u16 slot2Species, u16 *targetSpecies)
{
    u32 i;
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    const struct FormChange *formChanges = gFormChangeTablePointers[species];

    if (formChanges == NULL)
        return FALSE;

    for (i = 0; formChanges[i].method != FORM_CHANGE_END; i++)
    {
        switch (formChanges[i].method)
        {
        case FORM_ITEM_USE:
        case FORM_ITEM_USE_TIME:
            if (formChanges[i].param2 == slot2Species)
            {
                if (targetSpecies != NULL)
                    *targetSpecies = formChanges[i].targetSpecies;
                return TRUE;
            }
            break;
        }
    }

    return FALSE;
}

// returns SPECIES_NONE if no form change is possible
u16 GetFormChangeTargetSpecies(struct Pokemon *mon, u16 method, u32 arg, FormChangeResult* result)
{
    u32 i;
    u16 targetSpecies = SPECIES_NONE;
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    const struct FormChange *formChanges = gFormChangeTablePointers[species];
    u16 heldItem;
    u32 ability;

    if (formChanges != NULL)
    {
        heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
        ability = GetAbilityBySpecies(species, GetMonData(mon, MON_DATA_ABILITY_NUM, NULL));

        for (i = 0; formChanges[i].method != FORM_CHANGE_END; i++)
        {
            if (method == formChanges[i].method)
            {
                switch (method)
                {
                case FORM_ITEM_HOLD:
                    if (heldItem == formChanges[i].param1)
                        targetSpecies = formChanges[i].targetSpecies;
                    break;
                case FORM_ITEM_HOLD_MOVE:
                    if (arg == formChanges[i].param1)
                        targetSpecies = formChanges[i].targetSpecies;
                    if (result != NULL)
                    {
                        result->oldMove = formChanges[i].param2;
                        result->replaceMove = formChanges[i].param3;
                    }
                    break;
                case FORM_ITEM_USE:
                    if (arg == formChanges[i].param1) {
                        targetSpecies = formChanges[i].targetSpecies;
                        if (result != NULL)
                        {
                            result->fused = formChanges[i].param2;
                            result->unfused = formChanges[i].param3;
                        }
                    }
                    break;
                case FORM_MOVE:
                    if (MonKnowsMove(mon, formChanges[i].param1) != formChanges[i].param2)
                        targetSpecies = formChanges[i].targetSpecies;
                    break;
                case FORM_ITEM_HOLD_ABILITY:
                    if (heldItem == formChanges[i].param1 && ability == formChanges[i].param2)
                        targetSpecies = formChanges[i].targetSpecies;
                    break;
                case FORM_ITEM_USE_TIME:
                    RtcCalcLocalTime();
                    if (arg == formChanges[i].param1)
                    {
                        switch (formChanges[i].param2)
                        {
                        case DAY:
                            if (gLocalTime.hours >= 12 && gLocalTime.hours < 24) {
                                targetSpecies = formChanges[i].targetSpecies;
                                if (result != NULL)
                                {
                                    result->fused = formChanges[i].param2;
                                    result->unfused = formChanges[i].param3;
                                }
                            }
                            break;
                        case NIGHT:
                            if (gLocalTime.hours >= 0 && gLocalTime.hours < 12) {
                                targetSpecies = formChanges[i].targetSpecies;
                                if (result != NULL)
                                {
                                    result->fused = formChanges[i].param2;
                                    result->unfused = formChanges[i].param3;
                                }
                            }
                            break;
                        }
                    }
                    break;
                }
            }
        }
    }

    return species != targetSpecies ? targetSpecies : SPECIES_NONE;
}
